实践部分
我定义了以下四个类:
类定义
1
public class A
2
{
3
public virtual void Foo1()
4
{
5
Console.WriteLine("A.Foo1() was invoked.");
6
}
7
8
public void Foo2()
9
{
10
Console.WriteLine("A.Foo2() was invoked.");
11
}
12
}
13
14
public class B : A
15
{
16
public override void Foo1()
17
{
18
Console.WriteLine("B.Foo1() was invoked.");
19
}
20
21
public new virtual void Foo2()
22
{
23
Console.WriteLine("B.Foo2() was invoked");
24
}
25
}
26
27
public class C : B
28
{
29
public new void Foo1()
30
{
31
Console.WriteLine("C.Foo1() was invoked.");
32
}
33
}
34
35
public class D : C
36
{
37
public override sealed void Foo2()
38
{
39
Console.WriteLine("D.Foo2() was invoked.");
40
}
41
}
42
43
public class A2
{3
public virtual void Foo1()4
{5
Console.WriteLine("A.Foo1() was invoked.");6
}7

8
public void Foo2()9
{10
Console.WriteLine("A.Foo2() was invoked.");11
}12
}13

14
public class B : A15
{16
public override void Foo1()17
{18
Console.WriteLine("B.Foo1() was invoked.");19
}20

21
public new virtual void Foo2()22
{23
Console.WriteLine("B.Foo2() was invoked");24
}25
}26

27
public class C : B28
{29
public new void Foo1()30
{31
Console.WriteLine("C.Foo1() was invoked.");32
}33
}34

35
public class D : C36
{37
public override sealed void Foo2()38
{39
Console.WriteLine("D.Foo2() was invoked.");40
}41
}42

43

当运行如下代码时,会打印出什么?
1
C aD = new D();
2
A aC = new C();
3
4
aD.Foo1();
5
aD.Foo2();
6
aC.Foo1();
7
aC.Foo2();
C aD = new D();2
A aC = new C(); 3

4
aD.Foo1();5
aD.Foo2();6
aC.Foo1();7
aC.Foo2();结果是:
打印出的结果
1
C.Foo1() was invoked.
2
D.Foo2() was invoked.
3
B.Foo1() was invoked.
4
A.Foo2() was invoked.
5
C.Foo1() was invoked.2
D.Foo2() was invoked.3
B.Foo1() was invoked.4
A.Foo2() was invoked.5

例子很简单,依照之前的规则,可以画出如下一幅图。图中圆形的末端表示封闭、中断继承链;菱形的末端表示开放、允许构建继承链;类描述中的等式,表示从该类型的对象引用调用对应方法(等号左边的斜体)时,实际执行的代码体是在何处(等号右边的正常字体)定义的。

其实,为了确认这里描述出来的方法的继承链,甚至都不需要实地运行此代码。将代码放在Visual Studio里,使用“重构”(Refactor)菜单中的“重命名”(Rename)修改方法名称,待完成后就会发现在方法继承链的中断处,自动修改符号名称的动作也中止了。
补充
对于 this 关键字,上述的规则也适用。只需要将 this 依照当前的代码上下文翻译为对应的类型引用,就可以依照之前叙述的方法确定最终调用的代码了。例如在C中的Foo1方法里假如有这么一条语句:“this.Foo2()”。当在外部运行“D.Foo2()”时,就会就会解析到“C.Foo1()”,这时,C.Foo1()方法的内部在解析“this.Foo2()”时就会解析到D.Foo2()。
对于 base 关键字,则比较简单,只是在基类的方法(这里“基类的方法”一词,请参见“用词约定”的第6条。)中找到同名方法,然后调用,不存在解析虚函数的过程。
对于被委托对象包装的方法指针,在调用委托时,仍会按照上述规则解析到正确的方法。
本文示例中使用了“Foo”开头的方法名,而这个习惯借鉴自一些别的文章。这里是有个典故还是怎么?