CLR如何知道哪个方法调用从抽象基类继承的派生类?
问题描述:
我有以下的情况,我想知道的CLR如何知道调用哪个方法:CLR如何知道哪个方法调用从抽象基类继承的派生类?
public abstract class Shape
{
public abstract String PrintName();
}
public sealed class Square : Shape
{
public override String PrintName() { return "Square"; }
}
public sealed class Circle : Shape
{
public override String PrintName() { return "Circle"; }
}
于是我分别给出形状:
Shape square = new Square();
Shape circle = new Circle();
List<Shape> shapes = new List<Shape> { square, circle };
foreach (Shape s in shapes)
{
Console.WriteLine(s.PrintName());
}
// Output:
// Square
// Circle
那么是什么让我们可以调用方法派生类,即使我们正在调用基类型的方法?我很困惑这是如何处理的。
答
当您实例化Shape square = new Square();
时,square
确实是Square
的事实确实是完好无损的。请记住,变量square
确实是对真实对象的引用。就像您在这里一样,参考类型(在这种情况下,Shape
)必须与实例化类型(Square
)的继承层级相同或更高。
实例化后,当编译器看到square
时,它首先知道它是抽象类型Shape
,因为那是引用的类型。所以,它必须是Shape的子类型,因为你不能实例化一个抽象对象。既然你说new Square();
编译器会知道确切的类型。同样,一个对象的确切类型不会因为您将其分配给baser(更多基础)类型而丢失。
当你调用square.PrintName();
,编译器首先看到的是square
宣布与抽象类型Shape
,谁拥有的方法PrintName()
,也标志着抽象。这告诉编译器去查找子类中相同的确切方法。如果它在子类中找到PrintName()
,一切正常 - 将执行正确的功能。如果没有,你会得到一个错误,因为基类定义中的抽象词要求你实现它。
答
由于CLR的类型安全特性,它确保在创建Foo实例时不会将其视为Bar,因此在运行时它始终知道对象是什么类型。因此,当您在形状上调用PrintName()时,它知道它是处理Square还是Circle。
请注意,由于GetType()是非虚拟的,因此无法重写,因此您无法欺骗对象的类型。
我不认为编译器的工作是寻找虚拟方法的实现。该工作在运行时完成。虚拟机负责该工作,而不是编译器。即使你指的是JIT编译器,它仍然不准确。编译器只是检查有问题的类型是否有相应的方法,并将剩余的工作(动态查找)留给运行时。 –