【问题标题】:Why is implicit conversion allowed from superclass to subclass?为什么允许从超类到子类的隐式转换?
【发布时间】:2010-11-30 22:44:58
【问题描述】:

谁能告诉我为什么带有“//Compiles”的行可以编译,为什么带有“//Doesn't Compile”的行不能编译?

我不明白为什么 A 可以隐式转换为 B,而不是反过来。

public class SomeClass {

 static public void Test() {
  AClass a = new AClass();
  BClass b = new BClass();

  a = b; // Compiles
  b = a; // Doesn't compile
 }
}

public class AClass {
 public void AMethod() { 
     Console.WriteLine("AMethod");
 }
}

public class BClass : AClass { 
 public void BMethod() {
  Console.WriteLine("BMethod");
 }
}

谢谢!

【问题讨论】:

    标签: c# .net inheritance type-conversion implicit-conversion


    【解决方案1】:

    A 不能隐式转换为 B。B 可以转换为 A。

    foo = bar
    

    它将尝试将 'bar' 转换为 'foo' 的类型。

    (也就是说,我认为您只是误解了“赋值”在隐式转换方面的工作原理。)

    【讨论】:

      【解决方案2】:

      让我们将类的名称从 AClass 更改为 Mammal,将 BClass 更改为 Dog。

      
      a = b; // you're putting a dog on a variable of type Mammal. That's OK.
      b = a; // You're putting a mammal (could be a cat, a monkey, etc.) on a variable of type Dog.
      

      也许不是最好的例子,但可能足以让你理解。

      【讨论】:

      • 不去动物园就谈不上 OOP。 =)
      • 别提鸭嘴兽了!
      • @Mitch 你在开玩笑吗?你还应该如何谈论 MI?
      【解决方案3】:

      这与 C# 关系不大;这是基本的继承。 a 不是 BClass 类型。如果 BClass 有额外的字段/属性怎么办?当您尝试访问其中一个成员时会发生什么?

      【讨论】:

        【解决方案4】:

        因为BClass 的所有实例也是AClass,因为BClass 继承自AClassAClass 不如 BClass 特定,因此您可以隐式地从 B 转换为 A

        【讨论】:

          【解决方案5】:

          BClassAClass的子类(或者AClassBClass的超类),子类关系是“是”的关系。所以如果bBClass 的一个实例,那么它也是AClass 的一个实例。这就是为什么用变量a 指向b 是可以的,但用b 指向a 是不行的,因为这需要额外的假设。

          【讨论】:

            【解决方案6】:

            因为 B 做了 A 所做的一切,但 A 不一定做 B 所做的一切。这样想:

            AClass --> Shape
            BClass --> Circle
            
            Shape a = new Shape();
            Circle b = new Circle();
            
            a = b; // works because a is of type "Shape" and a circle is a specific shape
            b = a; // doesn't work because b is of type "Circle" and a could be a square.
            

            【讨论】:

              【解决方案7】:

              从类实例化的对象可以被视为其任何超类的类型,但不能被视为子类的类型

              • 可以将子类视为其超类,但绝不可反其道而行之。

              用更抽象的术语:

              public class HarleyExample
              {
                  static public void Test()
                  {
                      Motorcycle a = new Motorcycle();
                          HarleyDavidson b = new HarleyDavidson();
                          Motorcycle c = new Motorcycle(); //Just a plain motorcycle
                          a = b; // A Harley can be treated as a regular motorcycle
                          //b = a; // Not just any motorcycle is a Harley
              
                          Console.WriteLine("Is A a motorcycle?  " + (a is Motorcycle)); 
                          Console.WriteLine("Is A a harley?      " + (a is HarleyDavidson));
                          Console.WriteLine();
                          Console.WriteLine("Is B a motorcycle?  " + (b is Motorcycle));
                          Console.WriteLine("Is B a harley?      " + (b is HarleyDavidson));
                          Console.WriteLine();
                          Console.WriteLine("Is C a motorcycle?  " + (c is Motorcycle));
                          Console.WriteLine("Is C a harley?      " + (c is HarleyDavidson));
              
                          Console.ReadKey();
                  }
              }
              
              public class Motorcycle
              {
                  public void Cruise()
                  {
                      Console.WriteLine("Cruising");
                  }
              }
              
              public class HarleyDavidson : Motorcycle
              {
                  public void CruiseInStyle()
                  {
                      Console.WriteLine("Cruising in style on a Harley");
                  }
              }
              

              【讨论】:

              • 我想我不明白的是,当我调试它时,一旦 a = b 被执行,a 在调试器中显示为“HarelyDavidson”类型。我不知道这是怎么回事……
              • 简单。调试器调用 .GetType()。 Motorcycle 的 GetType() 返回 Motorcycle。哈雷戴维森的 GetType() 返回哈雷戴维森。你得到的是有效的类型,而不是声明的类型。
              • @Mike Christiansen,很好的说明
              • @HC: a = b 不会将哈雷戴维森“转换”成摩托车。 “a”和“b”都将引用同一个对象,但“b”将能够访问 HarleyDavidson 成员,而“a”将只能访问 Motorcycle 成员。实际对象仍然是哈雷戴维森。
              【解决方案8】:

              您可能对哪个是哪个感到困惑,因为您的问题是 “为什么允许从超类到子类的隐式转换?”

              实际上,恰恰相反。子类是超类的一个实例,但反之则不然,这就是类型不兼容的原因。

              想象一个只有一个方法或常量的非常小的超类。现在想象一个定义所有东西的子类,包括厨房水槽。这些几乎是完全不同的类型。但是,子类仍然是超类的实例;它确实有一个方法或常量。

              另一方面,超类几乎没有继承类实现的任何东西。“几乎”足以让父类仍然是子类的实例,但将父类传递给期望child 很可能无法工作,因为几乎没有可用的东西。

              【讨论】:

                【解决方案9】:

                有点转述其他人所说的话。我不知道这是否会让你更清楚。

                'a' 被声明为支持 AMethod() 方法的 AClass 类型的对象。

                'b' 被声明为 BClass 类型的对象,它支持 BMethod() 方法,并且作为 AClass 的子类,也将支持 AMethod() 方法,因为它从其父超类继承。

                因此,您可以轻松地将 BClass 类型的对象分配给 AClass 类型的变量,因为编译器希望只在其上调用 AMethod(),这很好。

                但是,您不能将 AClass 类型的对象分配给 BClass 类型的变量,因为编译器可能希望在其上调用 AMethod() 或 BMethod(),当然,它不会能够将后者作为 AClass 对象执行只是不支持它。

                【讨论】:

                  【解决方案10】:

                  这直接来自Liskov Substitution Principle

                  令 q(x) 是关于 T 类型的对象 x 的可证明属性。那么 q(y) 对于 S 类型的对象 y 应该为真,其中 S 是 T 的子类型

                  换句话说,派生类总是可以用来代替基类。它通常不可能反过来 - 因为基类不能做派生类所做的事情。

                  (我知道我在这里混淆了时间线——继承是第一位的,Liskov 是第二位——但她很好地说明了如何使用继承)

                  【讨论】:

                    猜你喜欢
                    • 2015-03-12
                    • 2018-02-05
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-06-10
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-06-17
                    相关资源
                    最近更新 更多