public class Base1
{
public virtual void f()
{
Console.WriteLine("Base1.f()");
}
}
public class Derived1 : Base1
{
// Hides Base1.f() because 'override' was not specified
public new virtual void f()
{
Console.WriteLine("Derived1.f()");
}
}
public class Derived2 : Derived1
{
// Overrides Derived1.f()
public override void f()
{
Console.WriteLine("Derived2.f()");
// Call base method
base.f();
}
}
class Program
{
static void Main(string[] args)
Base1 ob1 = new Derived1();
ob1.f();
Base1 ob2 = new Derived2();
ob2.f();
Derived1 ob3 = new Derived2();
ob3.f();
Derived2 ob4 = new Derived2();
ob4.f();
}
}
// Calls Derived2.f() because Derived2 overrides Derived1().f()
Derived1 ob3 = new Derived2();
ob3.f();
期待
Base1 ob2 = new Derived2();
ob2.f();
答案 0 :(得分:6)
编译期间静态分析使用的方法槽取决于变量(或表达式)的类型,而不是实际对象。变量ob2
的类型为Base1
,因此使用Base1
方法广告位。然后然后根据键入的内容(在该插槽上基本上是vtable)选择正确的覆盖。所以使用了基本功能。
要使用derived2函数,变量(或表达式)必须键入为Derived1
或子类。
答案 1 :(得分:3)
基本上,如果您用来调用f()
的变量的编译时类型为Base1
,它将调用基本方法 - 因为没有& #39; s实际上覆盖了它。
如果编译时类型为Derived1
或Derived2
,则会根据执行时间在Derived1
或Derived2
中调用相应的方法对象的类型...因为此时编译器将只发出对Derived1.f()
的虚拟调用,并且将在执行时进行覆盖。
是的,.NET使用vtables。
答案 2 :(得分:3)
这里的问题是你把东西混合得太多了。
基本上,这就是你所做的:
f
f
方法f
,这将覆盖第二个类中的那个,而不是基类中的那个。所以,当你说:
Base1 b = new Derived2();
b.f();
然后你总是(在这种情况下)会调用f
的基本实现,因为f
中被覆盖的Derived2
是不同 f
方法。名称相同,但它仍然是一种不同的方法。
原因是编译器会看到你正在调用的f
来自Base1
类,所以它会调用它。
由于没有课程覆盖Base1.f
,这就是您正在呼叫的课程。
在回答评论中的问题时,严格来说,该类将有两个虚拟方法,均命名为f。
然而,有一个被Derived1中引入的新镜头所掩盖。
你可以在课堂上选择要拨打的电话:
public void MethodInDerived1()
{
f(); // calls Derived1.f()
base.f(); // calls Base1.f()
}
但是,从外面看,你需要通过施法来“挑选”。
换句话说:
Derived1 d = new Derived1();
d.f(); // calls Derived1.f()
((Base1)d).f(); // calls Base1.f()
您还可以通过反射观察方法。如果您在LINQPad中执行以下代码,您会看到有两种名为f
的方法:
void Main()
{
typeof(Derived1).GetMethods().Dump();
}
public class Base1
{
public virtual void f()
{
Debug.WriteLine("Base1.f");
}
}
public class Derived1 : Base1
{
public virtual new void f()
{
Debug.WriteLine("Derived1.f");
}
}
public class Derived2 : Derived1
{
public override void f()
{
Debug.WriteLine("Derived2.f");
base.f();
}
}
此脚本的输出被截断(右侧有更多信息):