2012年1月19日

[.Net] 介面可以繼承介面,但結果卻不是我想的那樣


事情是這樣發生的,
有一天我們決定讓 Spring.Net 進入我們的生活,
於是衝擊到了 GridView + ObjectDataSource 的美好時光,
是的, Spring.Net 讓我們的 Business Object 都虛化了(ㄜ我想說的是IoC),
而 ObjectDataSource 預設是用這種方式產生BusinessObject的執行個體:
e.ObjectInstance = Activator.CreateInstance(method.Type)
想當然,還沒透過 Spring.Net Injection 進來,一執行就賞你一個:
並未將物件參考設定為物件的執行個體
這很正常,而我們也在他發生前就預料到,並加以處理,處理的方式很簡單,
就如同 Creating an ObjectDataSource Control Source Object 中提到的,
You can control how the source object is created by handling the ObjectCreating event of the ObjectDataSource control. You can create an instance of the source object, and then set the ObjectInstance property of the ObjectDataSourceEventArgs class to that instance. The ObjectDataSource control will use the instance that is created in the ObjectCreating event instead of creating an instance on its own.
一切看起來是那麼的美好,但現實往往都比想象中來得殘酷,
因為我們的 Business Object 希望可以方便被客製,
於是我們讓他看起來像這樣:

問題來了,看似正常,但一執行就被賞了一個 ObjectDataSource 找不到參數的非泛型方法,
雖然所有介面都定義在 Core.Business.IFooServices 裡,
但 Business.IFooServices 明明就有繼承 Core.BUsiness.IFooServices,
為什麼操作 Business.IFooServices 就告訴我找不到方法,
追查的結果發現,在 ObjectDataSource 裡是用 Reflection 的手法GetMethod並Invoke,


Dim methods As MethodInfo() = type.GetMethods((BindingFlags.FlattenHierarchy Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance))))

偏偏 type.GetMethods 遇到 Interface 就不管他老子是誰了!
即使有告訴他要 BindingFlags.FlattenHierarchy 也沒用,
於是就拜請 Google 大神,這才發現,
原來
介面是可以繼承介面,但結果卻不是我想的那樣。
詳細的內容,下面的參考資料裡,每位大大都寫的很清楚,就不多談了,

直接下結論
介面繼承介面時,衍生的介面不需要實作父介面(這看起來很像繼承),而介面可以繼承 0 - 多個介面(哇,多重繼承?其實他也不過是介面而已),實作衍生介面時,需要實作衍生介面及其父介面(下面這段Code故意用 VB.Net的寫法,很驚人(至少嚇到我了)的 Inherits 可以寫不只一次)。

Interface a

    Sub a1method()
End Interface

Interface b

    Inherits a
    Sub b1method()
End Interface

Interface c

    Inherits a
    Inherits b
    Sub c1method()
End Interface
Class ca
    Implements c
    Public Sub a1method() Implements a.a1method
    End Sub

    Public Sub b1method() Implements b.b1method
    End Sub

    Public Sub c1method() Implements c.c1method
    End Sub
End Class
而介面繼承的目的,只是告訴實作,在實作介面時,要連同其繼承的介面一併實作僅此而已
對介面而言,只紀錄其繼承的介面是誰,不會把父介面的宣告抄一分給自己,
所以,以上例而言,class ca實作的對象雖然是 c ,
但自動產生出來的程式,卻是各個 Interface(見上面程式粗體字的地方),
如果是C#,使用明確實作就不能實作c.b1method 及 c.a1method,因為c根本就沒有他們。

--
參考資料

  1. .Net Interfaces Are Not Classes
  2. Reflection Pop Quiz - Does interface inheritance exist?
  3. Interface “inheritance” in C#
  4. Type.GetMethods Method (BindingFlags)
  5. 13.1.2 Base interfaces
  6. Interface Design