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(),一切正常 - 将执行正确的功能。如果没有,你会得到一个错误,因为基类定义中的抽象词要求你实现它。

+0

我不认为编译器的工作是寻找虚拟方法的实现。该工作在运行时完成。虚拟机负责该工作,而不是编译器。即使你指的是JIT编译器,它仍然不准确。编译器只是检查有问题的类型是否有相应的方法,并将剩余的工作(动态查找)留给运行时。 –

由于CLR的类型安全特性,它确保在创建Foo实例时不会将其视为Bar,因此在运行时它始终知道对象是什么类型。因此,当您在形状上调用PrintName()时,它知道它是处理Square还是Circle。

请注意,由于GetType()是非虚拟的,因此无法重写,因此您无法欺骗对象的类型。