接口(interface)和类(class)是CLR中应用最为广泛的两个概念。灵活的应用接口,可以构造出各种经典的设计模式。
接口的语法并不复杂,本篇主要记录接口中一些容易忽略的地方,以及如何更好的使用接口。
主要内容:
接口的继承
显式接口
泛型接口和约束
接口和抽象类
当子类继承父类后,父类继承的接口也一并继承了过来。如下例中的类Sub
当子类继承父类后,子类可以再次继承父类已经继承的接口。如下例中的类Sub2
这两者的区别在对接口方法调用,参见下面代码中的注释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<code>using</code> <code>System;</code>
<code>sealed</code> <code>class</code> <code>CLRviaCSharp_15</code>
<code>{</code>
<code> </code><code>static</code> <code>void</code> <code>Main(</code><code>string</code><code>[] args)</code>
<code> </code><code>{</code>
<code> </code><code>Sub s =</code><code>new</code> <code>Sub();</code>
<code> </code><code>// 类Sub自身的Show方法</code>
<code> </code><code>s.Show();</code>
<code> </code><code>// 类Base的Show方法,即继承自IShowMessage的Show方法</code>
<code> </code><code>((Base)s).Show();</code>
<code> </code><code>// 类Base继承自IShowMessage的Show方法</code>
<code> </code><code>((IShowMessage)s).Show();</code>
<code> </code><code>Console.WriteLine(</code><code>"============================="</code><code>);</code>
<code> </code><code>Sub2 s2 =</code><code>new</code> <code>Sub2();</code>
<code> </code><code>// 类Sub2自身的Show方法,即继承自IShowMessage的Show方法</code>
<code> </code><code>s2.Show();</code>
<code> </code><code>// 类Base的Show方法</code>
<code> </code><code>((Base)s2).Show();</code>
<code> </code><code>// 类Sub2继承自IShowMessage的Show方法</code>
<code> </code><code>((IShowMessage)s2).Show();</code>
<code> </code><code>Console.ReadKey();</code>
<code> </code><code>}</code>
<code>}</code>
<code>interface</code> <code>IShowMessage</code>
<code> </code><code>void</code> <code>Show();</code>
<code>class</code> <code>Base : IShowMessage</code>
<code> </code><code>public</code> <code>void</code> <code>Show()</code>
<code> </code><code>Console.WriteLine(</code><code>"IShowMessage"</code><code>);</code>
<code>/// <summary></code>
<code>/// 当子类继承父类后,父类继承的接口也一并继承了过来</code>
<code>/// </summary></code>
<code>class</code> <code>Sub : Base</code>
<code> </code><code>// 类Sub本身的Show方法,与Base中继承IShowMessage的Show无关</code>
<code> </code><code>public</code> <code>new</code> <code>void</code> <code>Show()</code>
<code> </code><code>Console.WriteLine(</code><code>"Sub"</code><code>);</code>
<code>/// 子类继承父类后,子类可以再次继承父类已经继承的接口</code>
<code>class</code> <code>Sub2 : Base, IShowMessage</code>
<code> </code><code>// 类Sub2继承IShowMessage的Show方法,</code>
<code> </code><code>// 与Base中继承IShowMessage的Show无关</code>
<code> </code><code>Console.WriteLine(</code><code>"Sub2"</code><code>);</code>
<code>} </code>
在上例中,类Base继承IShowMessage之后,就不能定义与 IShowMessage中签名相同的方法了。
如上例中,类Base无法再定义与方法Show相同签名的方法了。
为了解决这种情况,C#中还提供了显式接口的定义方法。
<code> </code><code>Console.WriteLine(</code><code>"Base"</code><code>);</code>
<code> </code>
<code> </code><code>void</code> <code>IShowMessage.Show()</code>
这样,如果要调用IShowMessage.Show()方法,必须将Base类的实例转型成IShowMessage才行。
<code>Base b =</code><code>new</code> <code>Base();</code>
<code>b.Show();</code>
<code>((IShowMessage)b).Show();</code>
显示接口的作用主要如下:
1. 当两个接口有签名相同的方法时,一个类可以通过显示接口的方式来同时继承这两个接口。
<code>interface</code> <code>IPrintMessage</code>
<code>class</code> <code>Base : IShowMessage, IPrintMessage</code>
<code> </code><code>void</code> <code>IPrintMessage.Show()</code>
<code> </code><code>throw</code> <code>new</code> <code>NotImplementedException();</code>
2. 通过显式接口来增强类型安全性,从而减少装箱操作,提高性能
首先是实现隐式接口的例子:
<code> </code><code>Base b =</code><code>new</code> <code>Base();</code>
<code> </code><code>b.Show(10);</code>
<code> </code><code>void</code> <code>Show(Object o);</code>
<code> </code><code>#region IShowMessage Members</code>
<code> </code><code>public</code> <code>void</code> <code>Show(</code><code>object</code> <code>o)</code>
<code> </code><code>Console.WriteLine(o.ToString());</code>
<code> </code><code>#endregion</code>
在调用b.Show(10)时发生装箱操作,通过以下的IL代码(IL_00a)可以看出
<code>.method private static hidebysig</code>
<code> </code><code>void Main (</code>
<code> </code><code>string[] args</code>
<code> </code><code>) cil managed</code>
<code> </code><code>// Method begins at RVA 0x217c</code>
<code> </code><code>// Code size 28 (0x1c)</code>
<code> </code><code>.maxstack 2</code>
<code> </code><code>.entrypoint</code>
<code> </code><code>.locals init (</code>
<code> </code><code>[0] class Base b</code>
<code> </code><code>)</code>
<code> </code><code>IL_0000: nop</code>
<code> </code><code>IL_0001: newobj instance void Base::.ctor()</code>
<code> </code><code>IL_0006: stloc.0</code>
<code> </code><code>IL_0007: ldloc.0</code>
<code> </code><code>IL_0008: ldc.i4.s 10</code>
<code> </code><code>IL_000a: box int32</code>
<code> </code><code>IL_000f: callvirt instance void Base::Show(object)</code>
<code> </code><code>IL_0014: nop</code>
<code> </code><code>IL_0015: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()</code>
<code> </code><code>IL_001a: pop</code>
<code> </code><code>IL_001b: ret</code>
<code>} // End of method CLRviaCSharp_15.Main</code>
通过显示实现接口,可以在类Base中定义输出int型的Show方法,代码如下:
<code> </code><code>public</code> <code>void</code> <code>Show(</code><code>int</code> <code>i)</code>
<code> </code><code>Console.WriteLine(i.ToString());</code>
<code> </code><code>void</code> <code>IShowMessage.Show(</code><code>object</code> <code>o)</code>
查看Main函数的IL代码,已经没有了之前的装箱操作
<code> </code><code>// Code size 23 (0x17)</code>
<code> </code><code>IL_000a: callvirt instance void Base::Show(int32)</code>
<code> </code><code>IL_000f: nop</code>
<code> </code><code>IL_0010: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()</code>
<code> </code><code>IL_0015: pop</code>
<code> </code><code>IL_0016: ret</code>
显式接口还有一点需要注意的地方是,显式接口不能由派生类调用。
泛型是之前看过的概念,在接口中使用泛型,同样可以获得泛型带给我们的种种好处。
1. 提供类型安全性
比如IComparable接口和IComparable<Int32>接口相比,后者提供了类型安全检查。
<code>int</code> <code>x = 1;</code>
<code>IComparable c1 = x;</code>
<code>// 编译通过,但是运行时异常</code>
<code>c1.CompareTo(</code><code>"2"</code><code>);</code>
<code>IComparable<Int32> c2 = x;</code>
<code>// 编译不通过,类型不匹配</code>
<code>c2.CompareTo(</code><code>"2"</code><code>);</code>
2. 减少装箱次数
还是用IComparable接口和IComparable<Int32>接口做比较。
<code>int</code> <code>x = 1, y = 2;</code>
<code>// 此处装箱一次 (x装箱)</code>
<code>// 此处装箱一次 (y装箱)</code>
<code>c1.CompareTo(y);</code>
<code>// 此处不用装箱,因为类型参数T为Int32</code>
<code>c2.CompareTo(y);</code>
3. 一个类可以实现同泛型参数类型不同的同一个接口,如下所示
<code>class</code> <code>MyClass : IComparable<Int32>,IComparable<</code><code>string</code><code>></code>
<code> </code><code>#region IComparable<int> Members</code>
<code> </code><code>public</code> <code>int</code> <code>CompareTo(</code><code>int</code> <code>other)</code>
<code> </code><code>#region IComparable<string> Members</code>
<code> </code><code>public</code> <code>int</code> <code>CompareTo(</code><code>string</code> <code>other)</code>
“接口和基类的关系,以及何时使用接口,何时使用基类。”是在设计时经常需要考虑的问题。
已经有很多文章对此进行了讨论,这里要说的是这两件事可以同时做。
即:定义一个接口,同时提供一个实现了这个接口的基类。
.net Framework中的就有这样的例子,比如IComparable接口就有Comparable类提供的默认实现。
比如下面的例子,即使MyClass中没有实现IShowMessage的代码也无所谓,因为ShowMessage提供了默认实现。
<code>class</code> <code>ShowMessage : IShowMessage</code>
<code>class</code> <code>MyClass : ShowMessage , IShowMessage</code>
本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/07/28/2119737.html,如需转载请自行联系原作者