5

谁能告诉我为什么带有“//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");
 }
}

谢谢!

4

10 回答 10

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.
于 2009-09-24T00:07:29.367 回答
8

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

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

于 2009-09-24T00:05:09.693 回答
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");
    }
}
于 2009-09-24T00:09:03.313 回答
4

这直接来自Liskov 替换原则

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

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

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

于 2009-09-24T00:22:59.343 回答
1

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

foo = bar

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

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

于 2009-09-24T00:04:53.010 回答
1

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

于 2009-09-24T00:06:23.660 回答
1

因为 的所有实例BClass也是AClass因为 BClass 继承自AClass. AClass没有那么具体,BClass因此您可以隐式地从 B 转换为 A

于 2009-09-24T00:06:56.750 回答
1

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

于 2009-09-24T00:07:06.453 回答
1

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

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

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

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

于 2009-09-24T00:10:11.897 回答
1

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

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

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

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

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

于 2009-09-24T00:22:02.233 回答