天天看點

《CLR Via C# 第3版》筆記之(十五) - 接口

接口(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>/// &lt;summary&gt;</code>

<code>/// 當子類繼承父類後,父類繼承的接口也一并繼承了過來</code>

<code>/// &lt;/summary&gt;</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&lt;Int32&gt;接口相比,後者提供了類型安全檢查。

<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&lt;Int32&gt; c2 = x;</code>

<code>// 編譯不通過,類型不比對</code>

<code>c2.CompareTo(</code><code>"2"</code><code>);</code>

2. 減少裝箱次數

還是用IComparable接口和IComparable&lt;Int32&gt;接口做比較。

<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&lt;Int32&gt;,IComparable&lt;</code><code>string</code><code>&gt;</code>

<code>    </code><code>#region IComparable&lt;int&gt; Members</code>

<code>    </code><code>public</code> <code>int</code> <code>CompareTo(</code><code>int</code> <code>other)</code>

<code>    </code><code>#region IComparable&lt;string&gt; 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,如需轉載請自行聯系原作者

繼續閱讀