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

时间:2009-09-24 00:01:20

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

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

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

谢谢!

10 个答案:

答案 0 :(得分:15)

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

答案 1 :(得分:8)

让我们将类的名称从A Class更改为Mammal,将B Class更改为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.

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

答案 2 :(得分:4)

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

  • 可以将子类视为其超类,但从不反向。

用更抽象的术语来说:

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");
    }
}

答案 3 :(得分:4)

这直接来自 Liskov Substitution Principle

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

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

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

答案 4 :(得分:1)

A不可隐式转换为B. B可转换为A.

foo = bar

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

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

因为BClass的所有实例都是AClass,因为BClass继承自AClassAClass的具体性不如BClass,因此您可以隐式地将B转换为A

答案 7 :(得分:1)

BClassAClass的子类(或AClassBClass的超类),子类关系是“是一种”关系。因此,如果bBClass的实例,则它也是AClass的实例。这就是使用变量b指向a的原因,但是a指向b是不行的,因为这需要额外的假设。

答案 8 :(得分:1)

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

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

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

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

答案 9 :(得分:1)

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

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

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

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

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