我有以下情况,我想知道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
那么即使我们在基类型上调用方法,我们怎么能在派生类上调用该方法呢?我很困惑这是如何处理的。
答案 0 :(得分:2)
由于CLR的类型安全功能,它确保在创建Foo实例时不能将其视为Bar,在运行时它始终知道对象的类型。因此,当您在形状上调用PrintName()时,它会知道它是处理方形还是圆形。
请注意,由于GetType()是非虚拟的,因此无法覆盖它,因此您无法欺骗对象的类型。
答案 1 :(得分:1)
当您实例化Shape square = new Square();
时,square
确实是Square
的事实确实完好无损。请记住,变量square
实际上是对真实对象的引用。引用类型(在本例中为Shape
)必须与继承层次结构相同或更高,而不是实例化类型(Square
),如此处所示。
在实例化之后,当编译器看到square
时,它首先知道它是抽象类型Shape
,因为它是引用的类型。因此,它必须是Shape的子类型,因为您无法实例化抽象对象。由于你说new Square();
编译器会知道确切的类型。同样,对象的确切类型不会因为您将其分配给baser(更多基础)类型而丢失。
当您调用square.PrintName();
时,编译器首先看到square
声明为抽象类型Shape
,其方法PrintName()
也标记为抽象。这告诉编译器去寻找子类中相同的精确方法。如果它在子类中找到PrintName()
,一切都很好 - 将执行正确的函数。如果没有,则会出现错误,因为基类定义中的单词abstract要求您实现它。