接口方法的隐式和显示实现
先看一段代码,先猜猜下面标注1、2、3、4、5、6、7、8、9、10 的地方的输出情况
注:使用 Snippet Compiler Live 2008 编辑器
1 public static void RunSnippet()
2 {
3 ITest a = new Test();
4 a.Say(); // 1
5
6 a = new App();
7 a.Say(); // 2
8
9 a = new Strong();
10 a.Say(); // 3
11
12 Test t = new Test();
13 t.Say(); // 4
14
15 t = new App();
16 t.Say(); // 5
17 ((ITest)t).Say(); // 6
18
19 t = new Strong();
20 t.Say(); // 7
21 ((ITest)t).Say(); // 8
22
23 Strong s = new Strong();
24 s.Say(); // 9
25 ((ITest)s).Say(); // 10
26 }
27
28 public class Test : ITest
29 {
30 public void Say()
31 {
32 Console.WriteLine("Test Says.");
33 }
34
35 void ITest.Say()
36 {
37 Console.WriteLine("ITest Says.");
38 }
39 }
40
41 public class App : Test, ITest
42 {
43 new public void Say()
44 {
45 Console.WriteLine("App says.");
46 }
47 }
48
49 public class Strong : Test/*, ITest*/
50 {
51 /*
52 new public void Say()
53 {
54 Console.WriteLine("Strong says.");
55 }
56 */
57 }
58
59 public interface ITest
60 {
61 void Say();
62 }
输出结果为:
1 ITest Says. (接口显示实现,接口类型只调用接口方法)
2 App Says. (虽然继承Test,但重新继承了接口,并重写了接口方法)
3 ITest Says. (继承Test,接口显示实现的方法也继承)
4 Test Says. (Test对象,调用对象方法,而不是接口方法)
5 Test Says. (为什么不是App Says? 因为App重新实现的是接口方法,而Test中的接口方法是不能重写的,故调用是Test对象的方法)
6 App says. (为什么不是ITest Says? 因为App实现了接口方法,接口调用的是App实现接口方法,再说Test中的显示接口方法是私有的)
7 Test Says. (继承的是Test类型的方法)
8 ITest Says. (接口类型调用显示接口方法)
9 Test Says. (继承的是Test对象方法)
10 ITest Says. (接口类型调用显示接口方法)
为什么会出现上面的结果呢?
当一个类型实现接口的时候,C#编译时同时生成了一个类型的对象方法和接口方法,他们两个引用的实现是同一个方法。
当显示实现接口方法,并同时实现类型的对象方法时就区分开来了,并且接口方法的访问属性被编译器生产元数据时指定为私有的,防止类型实例调用。这样显示接口方法只能用接口类型调用,反之,当用接口类型调用方法时,只能调用接口的方法(无论显示还是隐式实现)。
另一点,显示接口方法是不能被派生类重写的,因为显示接口方法并不属于类型的对象模型的一部分,它是将一个接口(一组行为或方法)连接到一个类型上,同时避免暴露行为/方法的一种方式。
参考《框架设计(第二版) CLR Via C#》第14张5节(14.5) 接口方法的隐式和显示实现(幕后细节)