有人可以告诉我为什么用“// 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");
}
}
谢谢!
答案 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继承自AClass
。 AClass
的具体性不如BClass
,因此您可以隐式地将B转换为A
答案 7 :(得分:1)
BClass
是AClass
的子类(或AClass
是BClass
的超类),子类关系是“是一种”关系。因此,如果b
是BClass
的实例,则它也是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对象执行后者只是不支持它。