程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 接口映射的實現及原理

接口映射的實現及原理

編輯:關於C#

看了Allen Lee,基類與接口混合繼承的聲明問題,發表一下個人看法。

先運行如下代碼:

namespace Test
{
  using System ;

  interface 接口
  {
     void 方法();
  }

  class 基類:接口
  {
     public void 方法()
    {
      Console.WriteLine("這是基類的方法哦!");
    }

    void 接口.方法()
    {
      Console.WriteLine("我是接口專門的實現方法哦,你不要逃避嘛。");
    }
  }

  class 沒有接口的繼承:基類
  {
    /**//// <summary>
    /// 這裡只有通過new才能夠用新的方法來覆蓋
    /// </summary>
    public new void 方法()
    {
      Console.WriteLine("我只能夠使用 new 來屏蔽它,不過,在IDesign的編程規范中,不推薦用 new。");
    }
  }

  class 有接口的繼承: 基類, 接口
  {
     void 接口.方法()
    {
      Console.WriteLine("這就叫做接口的重映射,這只是技巧性的東西而已。");
    }
  }

  class TestClass
  {
    [STAThread]
    static void Main(string[] args)
    {
      基類 c1 = new 基類();
      c1.方法();

      接口 i1 = new 基類();
      i1.方法();

      Console.WriteLine();

      沒有接口的繼承 c2 = new 沒有接口的繼承();
      c2.方法();

      接口 i2 = new 沒有接口的繼承();
      i2.方法();

      Console.WriteLine();

      有接口的繼承 c3 = new 有接口的繼承();
      c3.方法();

      接口 i3 = new 有接口的繼承();
      i3.方法();

      Console.Read();
    }
  }
}

我們先看看 該代碼的類圖的層次結構吧

可以看出,每一個“方法()”,實際上都是不同的。

在“有接口的繼承”中,接口將“Test.接口.方法()”進行了重新映射。這個映射是如何完成的呢?

看看如下的IL代碼:

在采取使用它們自己的類作為訪問入口時,代碼實現如下:

沒有接口的繼承,它實現的方式如下:

.method public hidebysig instance void 方法() cil managed
{
      // Code Size: 11 byte(s)
      .maxstack 1
      L_0000: ldstr "\u6211\u53ea\u80fd\u591f\u4f7f\u7528 new \u6765\u5c4f\u853d\u5b83\uff0c\u4e0d\u8fc7\uff0c\u5728IDesign\u7684\u7f16\u7a0b\u89c4\u8303\u4e2d\uff0c\u4e0d\u63a8\u8350\u7528 new\u3002"
      L_0005: call void [mscorlib]System.Console::WriteLine(string)
      L_000a: ret 
}

而有接口的繼承,實現方式是這樣的:

.method private hidebysig newslot virtual final instance void Test.接口.方法() cil managed
{
      .override Test.接口::方法
      // Code Size: 11 byte(s)
      .maxstack 1
      L_0000: ldstr "\u8fd9\u5c31\u53eb\u505a\u63a5\u53e3\u7684\u91cd\u6620\u5c04\uff0c\u8fd9\u53ea\u662f\u6280\u5de7\u6027\u7684\u4e1c\u897f\u800c\u5df2\u3002"
      L_0005: call void [mscorlib]System.Console::WriteLine(string)
      L_000a: ret 
}

在這裡可以看出,有接口的繼承實際上對“方法”進行override,但這個override是覆蓋的接口的方法的實現成員,並非是類的方法成員。

然後,我們繼續看看在客戶類中對兩者進行調用的IL

沒有接口繼承的IL

L_001d: newobj instance void Test.沒有接口的繼承::.ctor() //創建實例

L_0022: stloc.2
   L_0023: ldloc.2

L_0024: callvirt instance void Test.沒有接口的繼承::方法()

有接口繼承的IL

L_003a: newobj instance void Test.有接口的繼承::.ctor() //創建實例

L_003f: stloc.s 有接口的繼承1
   L_0041: ldloc.s 有接口的繼承1

L_0043: callvirt instance void Test.基類::方法()

如果是采取接口訪問時,則狀況如下:

沒有接口繼承的IL

L_0029: newobj instance void Test.沒有接口的繼承::.ctor()

L_002e: stloc.3
   L_002f: ldloc.3

L_0030: callvirt instance void Test.接口::方法()

有接口繼承的IL

L_0048: newobj instance void Test.有接口的繼承::.ctor()

L_004d: stloc.s 接口3
   L_004f: ldloc.s 接口3

L_0051: callvirt instance void Test.接口::方法()

----------------------------------------------------------------------------

注意一下上面標記顏色的部分,可以看出,采用不同的訪問方式,實現的結果並不盡相同。也就是說,針對接口實現了的方法,與類本身自帶的實現,是兩回事,這種情況很類似於采用new關鍵字進行創建一個新的同名成員方法時遭遇的問題。

結果之所以會不同,是由於訪問的方式不同的原因,如果采用“基類”來訪問,很明顯,這裡獲得的就是基類的成員實現。也就是說,這也是多態的一種體現,但並非不可預知或不可控制的。

看見有人說VB很難實現接口的映射,實際上並不是這樣,要解釋一下這個問題,這裡不得不說一下,強類型的C#語法的含義。

“基類 objBase = new 基類();” 這種語法,表示的是使用“基類”來訪問新建的“基類”的實例,換而言之,“基類 objDevired = new 沒有接口的繼承();”表示的就是使用基類來訪問新建的“沒有接口的繼承”的實例。

接口的訪問,也是如此,在原理上,將接口看作是一個十分特殊的抽象類,它與一般的抽象類的區別在於強制了成員的實現(接口的語法由編譯器來驗證的,在CLR並未提供限制性機制),所以,在有一些設計模式中,也可以看到采用了接口-抽象類-具體類的方式來繞開這類強制成員實現的檢查,從而提高靈活性。

所以,VB.NET照樣也可以完成接口映射,因為接口本身的實現機制仍舊是依賴於類的基本特征的。

示例偽代碼如下:

Friend Interface 接口Interface 接口
   ' Methods
   Sub 方法()Sub 方法()
End Interface

Friend Class 基類Class 基類
   Implements 接口

   Public Sub New()Sub New()
   Private Sub Test()Sub Test.接口.方法() Implements 接口.方法
   Public Sub 方法()Sub 方法()
End Class

Friend Class 沒有接口的繼承Class 沒有接口的繼承
   Inherits 基類

   Public Sub New()Sub New()
   Public Sub 方法()Sub 方法()
End Class

Friend Class 有接口的繼承Class 有接口的繼承
   Inherits 基類
   Implements 接口

   Public Sub New()Sub New()
   Private Sub Test()Sub Test.接口.方法() Implements 接口.方法
End Class

但就接口本身來說,是一個便利性的工具,在編程時的認識上,我們不應該將它與類視作“同一類東西”,它是對類的之間關系的一個描述。對於類本身的機制來說,繼承與多態都是提供父子關系的縱向關系描述,而橫向的關系描述,則反映得並不是很好,接口通過一定的實現機制,則部分性地彌補了這個空缺。

像代碼中所描述那樣,接口僅是作為一個便利性的工具存在,在面向接口的編程中,提供一切都圍繞接口而進行,所以,在此類的編程模式下,可以采用接口為替換一個方法實現提供十分理想的方式,雖然這比起濫用繼承並沒有什麼優勢,但它是一個思想與觀念上的轉變,從本質上來說,接口映射與繼承中的new一樣,作為技巧用用可以,但它本身並不是一個值得推薦的方式。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved