为什么允许从超类到子类的隐式转换?

有人可以告诉我为什么“//编译”的行编译,以及“//不编译”的行为什么不编译?

我不明白为什么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"); } } 

谢谢!

因为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. 

让我们将类的名称从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. 

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

这直接来自Liskov替代原则

设q(x)是关于类型T的对象x可certificate的属性。对于类型S的对象y,q(y)应该为真,其中S是T的子类型

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

(我知道我在这里混淆了时间表 – inheritance是第一次,Liskov排在第二位 – 但她很好地说明了如何使用inheritance)

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

  • 子类可以被视为其超类,但从不相反。

用更抽象的术语:

 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. B可转换为A.

 foo = bar 

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

(也就是说,我认为你只是误解了’赋值’在隐式转换方面是如何工作的。)

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

因为BClass所有实例都是AClass因为BClassinheritance自AClassAClassBClass更不具体,因此您可以隐式地将B从B转换为A.

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

解释其他人所说的话。 我不知道这是否会让你更清楚。

‘a’被声明为AClass类型的对象,它支持AMethod()方法。

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

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

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

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

实际上,这是相反的方式。 子类是超类的一个实例,但不是相反,所以这就是类型不兼容的原因。

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

另一方面,超类几乎没有inheritance类实现的任何东西。“Almost”足以让父级仍然是子类的实例,但将父类传递给期望子级的方法最多可能不起作用,因为几乎没有任何东西可用。