【问题标题】:Why explicit conversion required to assign base class to derived class? But not required to do the reverse为什么将基类分配给派生类需要显式转换?但不需要做相反的事情
【发布时间】:2012-01-21 16:37:23
【问题描述】:

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

public class animal
{
    public string name { get; set; }
}

public class dog : animal
{
    public int age { get; set; }
    public string type { get; set; }
}

用途:

animal a = new animal();

dog d = new dog();

a = d; //compiled

d = a; //Error:Cannot implicitly convert type 'animal' to 'dog'.

d = (dog)a; // compiled

派生类可以分配给基类但需要进行反向显式转换的内部发生了什么?即使基类和派生类都包含相同的成员,也发现相同的结果。

【问题讨论】:

    标签: c# types variable-assignment base derived


    【解决方案1】:

    所有的狗都是动物,但并非所有的动物都是狗。不允许隐式转换,因为您的 a 可能实际上不是 d。

    【讨论】:

    • 感谢 Marty..实际上我正在寻找一些内部解释...内存分配中发生了什么...为什么 CLR 不能允许隐式转换...以及与 CLR、内存相关的所有内部工作管理...
    【解决方案2】:

    当您使用显式转换时,就您而言,您是在告诉编译器您知道发生了什么。如果您要编译代码,删除d = a;,并尝试访问d 上的公共成员,则会引发异常。这是因为,在这种情况下,animal 不是dog。您明确创建了一个animal 并将其分配给a

    显式转换允许您在知道转换有效时执行操作。

    public class MyClass
    {
        public animal MyAnimal { get; private set; }
    
        public MyClass ()
        {
            MyAnimal = new dog ();
        }
    
        public void SetAge (int Age)
        {
            ((dog)MyAnimal).age = Age;
        }
    }
    

    在这种情况下,我们知道MyAnimal 实际上是dog,所以我们可以对其进行显式转换。由于继承,所有这些都有效。因为继承不能双向工作,所以编译器不允许隐式向上转换——你必须明确地这样做。

    我们知道,当一个类从其基类继承时,它会附带公共属性和方法。您不能说您对animal 的引用将始终包含dog 的属性和方法。

    【讨论】:

      【解决方案3】:

      这是一个简单的继承转换规则。例如:Dog is Animal 但反过来不正确 - Animal is Dog。 Animal 可能有多个子类。

      class Animal {}
      class Dog : Animal {}
      class Cat : Animal {}
      
      Animal a=new Cat(); // OK
      a=new Dog(); // OK
      
      Cat c=a; //Invalid - var a might contains reference of Cat or Dog
      Cat d=(Cat)a; // Throws InvalidCastException exception because "a" has ref. of Dog
      

      因此,超类引用变量可以在不强制转换的情况下保存子类对象的引用,但是当您将超类引用变量的引用分配给子类引用变量时,您必须进行强制转换。

      查看 MSDN 文章 - How to: Safely Cast by Using as and is Operators (C# Programming Guide).

      【讨论】:

        【解决方案4】:

        这样想,所有的狗都已经是动物,但并非所有的动物都是狗。要将动物分配给狗,您必须先将其转换为狗。

        【讨论】:

        • 让我们把所有的动物都变成狗吧:D
        【解决方案5】:

        派生类可以分配给基类但需要进行反向显式转换的内部发生了什么?即使基类和派生类都包含相同的成员,也发现相同的结果。

        无论哪种情况,内部几乎都没有发生任何事情。对象未被修改或复制;相反,只是对它的引用被存储在具有不同类型的新变量中。

        允许从doganimal 的隐式转换,因为如果dog 变量可以保存对对象的引用,那么该对象必须是dogpoodle 或诸如此类,所以animal 变量也可以持有对它的引用。

        相比之下,需要从animaldog 的显式转换,因为animal 对象实际上可能不是dog,在这种情况下运行时系统将引发异常。 (还记得我说过“内部几乎什么都没有发生”吗?它是“几乎什么都没有”,因为运行时系统必须检查以确保对象是 dog,并引发否则例外。)

        这是一个语言设计决定——我们绝对可以创建一种与 C# 完全一样的语言,除了 dog d = a; 等同于 dog d = (dog) a;,但 C#(和许多类似语言)的设计者认为它会令人困惑dog d = a; 有时会引发运行时异常。显式转换清楚地表明,除非a 引用dog,否则可能出现异常。

        【讨论】:

          猜你喜欢
          • 2014-09-19
          • 2020-06-29
          • 1970-01-01
          • 2017-12-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-18
          • 2011-05-08
          相关资源
          最近更新 更多