【问题标题】:why this downcast fail in C#?为什么这种沮丧在 C# 中失败了?
【发布时间】:2017-07-13 18:43:16
【问题描述】:

我有一个基类和派生类,如下所示

 public class BaseClass
{
    public int No { set; get; }
}

public class Derived : BaseClass
{
    public string Name { set; get; }
}

当我创建基类的实例并希望将其向下转换为派生类时,它返回 InvalidCastOperation。

class Program
{
    static void Main( string[] args )
    {
        BaseClass bas = new BaseClass();
        Derived derived = (Derived)bas; // invalid cast operation. why?
        System.Console.WriteLine();
    }
}

我想知道为什么它不起作用?是实现此请求的解决方案吗?

【问题讨论】:

  • 这应该叫upcasting
  • 所有 Derived 都是 BaseClass 但并非所有 BaseClass 都是 Derived
  • 您正在初始化基类并尝试将其转换为派生类。基类没有关于派生类的任何信息,也没有与派生类相关的属性。所以做不到。派生类可以转换为基类,但反之亦然。如果您在此处解释您要实现的目标会更好。

标签: c#


【解决方案1】:

这是因为你需要创建一个 Derived 的实例,如果你想能够转换回它:

BaseClass bas = new Derived();

然后您将能够将bas 实例(声明为BaseClass 类型)转换为具体的Derived 类,它是它的一个实例:

Derived derived = (Derived)bas;

【讨论】:

    【解决方案2】:

    使用更有意义的名称:

    class Fruit { }
    class Apple : Fruit { public int PitCount; }
    
    Fruit f = new Fruit(); // f is just some undetermined Fruity thing
    Apple a = (Apple) f;   // why would it be an Apple? Where would PitCount come from?
    

    防止某些此类错误的方法是制作 Fruit abstract
    f 可以来自各种来源时,更常见的使用模式是:

    if (f is Apple)
    {
        Apple a = (Apple) f; // now it will work
    }
    

    【讨论】:

    • 很好的例子。感谢其他朋友的回复
    【解决方案3】:

    您创建了BaseClass 的实例并尝试将其转换为Derived。当然失败了。每个Derived 也是一个BaseClass,但绝对不是相反。

    如果你这样做了

    BaseClass bas = new Derived();
    Derived derived = (Derived) bas;
    

    它会起作用,因为实例实际上Derived 的一个实例。

    【讨论】:

      【解决方案4】:

      继承的规则是:

      • 派生类的每个实例也是基类的一个实例
      • 反之则不适用。并非每个基类也是一个特定的派生类。它可能只是基类的一个实例(如您的示例),也可能是从 Base 派生的另一个类的实例。

      修正你的例子:

      BaseClass bas = new Derived();
      Derived derived = (Derived)bas; // or better: = bas as Dervied
      

      【讨论】:

      • 为什么bas as Derived 在您看来应该更好?我们已经知道 bas实际上是Derived的一个实例,不需要安全转换和检查null
      • 在这种情况下是的 - 没关系。只是我通常更喜欢安全强制转换(和类型检查)而不是简单地抛出异常。
      • 类型检查 安全转换是没用的,你最好不要使用 as-cast 并检查 null 或者如果你真的想要类型检查然后使用直接转换。但这超出了问题或此答案的范围,仅此而已。
      • 这就是我的意思。 as cast plus null 检查对我来说是一种类型检查。 is 运算符是另一个 - 取决于具体情况。
      【解决方案5】:

      您不能将基对象强制转换为派生类型,因为该对象不是派生类型。反过来也行,因为DerivedType 对象本质上也是BaseType。反之亦然 - BaseType 不一定是 DerivedType

      这样想。你可以这样做:

      Square a = new Square();
      Shape b = (Shape)a;
      

      因为继承允许它发生 - 正方形一个形状。

      但是,尝试这样做:

      Shape a = new Shape();
      Square b = (Square)a;
      

      行不通,因为即使正方形是一种形状,三角形、圆形、平行四边形或梯形也可以这样说。能够采用任何随机形状并说它可以是其中的 any 是没有意义的,因为这与说它是 all 是一样的,这显然不是真的——正方形不是,也永远不会是三角形。

      【讨论】:

        【解决方案6】:

        当你写这篇文章时你期望什么:

        object o = new object();
        MyClass m = (MyClass) o;
        

        当然这会失败,因为o 只是一个object,对MyClass 没有任何了解。特别是o 不知道如何设置MyClass 中定义的字段和属性,因此在创建o 时,您可以只设置object 中定义的那些成员,而不是MyClass 中定义的那些成员。因此,如果演员阵容成功,会发生什么?您有一个只有这些成员的 object 实例,将其转换为 MyClass 并获得后者的无效实例,因为它的任何成员都没有被初始化。

        换句话说:MyClass 的每个实例都是 object,但当然不是每个object 都是 MyClass

        你有几个机会。

        1. 创建派生类的实例

          BaseClass b = new Derived();
          
        2. 创建一个复制构造函数,它基于BaseClass 的当前实例创建一个新的Derived 实例:

          public class BaseClass
          {
              public int MyProperty { get; set; }
          }
          
          public class Derived : BaseClass
          {
              public string AnotherProperty { get; set;}
          
              public Derived(BaseClass b)
              {
                  this.MyProperty = b.MyProperty;
                  this.AnotherProperty = "MyValue";
              }
          }
          
        3. 使用一些基于反射的代码来创建Derived 的新实例并从BaseClass 的现有实例复制所有属性和字段。这与 2 非常相似,但避免了您必须在代码中自己设置成员。

        【讨论】:

          【解决方案7】:

          Derived 扩展了 BaseClass

          您使用一个属性创建Basclass

          当您尝试将其转换为 Derived 时,尚未创建属性 Name(可以这么说)

          如果你想尝试

           Derived derived = new Derived();
           BaseClass bas =(BaseClass)derived;
           System.Console.ReadLine();
          

          它会起作用,因为派生扩展了基础,因此在创建派生时,它同时将 NameNo 强制转换为 BaseClass 有效地删除了 Name 变量。

          铸造无法像您尝试那样添加变量。它希望它在那里,但它不是。

          【讨论】:

            【解决方案8】:

            要回答为什么会失败,基本上是因为你的Derived EXTENDS 你的BaseClass。这意味着它添加了更多(功能、字段...),而这些在您的BaseClass 中不存在。为此,编译器必须神奇地创建一个包含此扩展的新实例,然后将其分配给您的字段/变量。

            收拾东西。每当您创建对象的实例时,它都会创建该类型内的所有成员(包括基本类型元数据)。由于您的BaseClass 是作为该类型创建的,因此它对Derived 一无所知。更不用说您的 Derived 对象引入的新属性。

            // BaseClass :
            //  - metadata of this type and System.Object ( because every object derives from System.Object in C# )
            //  - field info of "No" property's backing field 
            //  - property info "No" property
            
            // Derived :
            //  - metadata of this type and BaseClass type
            //  - field info of "Name" property's backing field 
            //  - property info "Name" property
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-07-12
              • 1970-01-01
              • 2014-11-08
              • 1970-01-01
              • 2011-06-16
              • 2023-01-27
              • 2020-11-12
              • 2020-05-05
              相关资源
              最近更新 更多