考虑以下代码:
public class Person
{
public virtual void UpdateLastLogin()
{
// business of last login here
}
}
public class Student : Person
{
public override void UpdateLastLogin()
{
// logging something specific to person
((Person)this).UpdatelastLogin();
}
}
为什么上面的代码抛出STACKOVERFLOW异常?
但这不是:
public class Person
{
public virtual void UpdateLastLogin()
{
// business of last login here
}
}
public class Student : Person
{
public override void UpdateLastLogin()
{
// logging something specific to person
base.UpdatelastLogin();
}
}
答案 0 :(得分:16)
这是因为 base 关键字禁用了虚拟方法调用,但是没有投射。
让我们分解一下:
使用virtual
和override
,C#有一个算法来查找正确的调用方法。该算法称为虚方法调用。
当C#编译器要调用具有override
修饰符的方法时,它会尝试查找最重写方法。也就是说,它沿着运行时类型的继承层次结构向下找到最重写的方法。在这种情况下,当您将Student
强制转换为基类Person
时,在运行时中,您真正拥有的是来自Person
的派生类。因此,C#编译器尝试查找最重写方法,即Student
类中存在的方法,并调用该方法。但是该方法再次尝试将Student
转换为Person
,然后链条继续进行。因此,您会看到StackOverflowException
。
但是,当您使用base
调用重写的基本方法时,编译器会将该方法(Person.UpdateLastLogin
)视为非虚方法< / strong>并且不会将虚拟方法调用和解决方案应用于它。
C#规范对override
和virtual
有一个非常合理的解释。我建议您阅读 17.5.3虚拟方法和 17.5.4覆盖方法部分。具体引用规范:
base-access禁用虚拟调用机制,只是将基本方法视为非虚方法。
答案 1 :(得分:4)
如果您要调用基类实现,请使用base
keyword:
base.UpdatelastLogin();
将引用转换为基类,实际上不执行任何操作。这就是面向对象编程的美妙之处!你不需要知道什么类型的东西 - 你只需要在一个对象上调用一个方法,这就是该对象的实现方式。
实例化子类后,无论引用的类型如何,该对象的UpdateLastLogin()
{{1}}中的条目总是指向子类。