none
有關介面相互繼承問題 RRS feed

  • 問題

  • 在查看msdn時注意到

    ICollection介面繼承IEnumerable介面,而IList介面繼承ICollection介面

    為何IList還要繼承IEnumerable介面呢



    • 已編輯 Hans Chen 2012年11月19日 上午 06:28
    2012年11月19日 上午 05:35

解答

  • 我和忠成討論了一下覺得這可能是某些因素造成的:

    (1) 一個可能原因是: C# 編譯器在 IL Code 中會將所有Interface 展開, 所以它文件乾脆也攤平寫.

    (2) 一個證據是 ArrayList 的 Source Code 中其實它是這樣宣告的

      public class ArrayList : IList, ICloneable

    但是在文件庫上的寫法也是整個攤開變成

      public class ArrayList : IList, ICollection, IEnumerable, ICloneable

    (3) 所以當你從 IL Code 反解回原始碼, 它就順勢被攤平. 所以我做了一個實驗

    我先寫一個類別庫的專案, 裡面寫了一個 Interface

    public interface IOnlyCollection:ICollection
    
    	{
    
    	}

    然後我將這個類別庫編譯完成後, 用 Reflector 去開啟這個 DLL, 然後看到的 Code 是

    public interface IOnlyCollection : ICollection, IEnumerable
    {
    }
    
     
    所以單純繼承 ICollection 和 同時繼承 ICollection 與 IEnumerable 意義上根本是一樣的 .

    註: 我另外發現這情形在 Visual Basic 的編譯器上似乎不會有相同的情況.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。



    • 已提議為解答 KKBruceMVP 2012年11月20日 上午 01:01
    • 已標示為解答 Hans Chen 2012年11月20日 上午 07:04
    • 已編輯 Bill ChungMVP, Moderator 2012年11月21日 上午 03:43 Reflector 誤植為 Reflection
    2012年11月19日 上午 11:13
    版主
  • 依照我的認知,介面實作介面的用意並不是要求介面實作,而是要求實作此介面的類別,必須要同時符合原介面以及介面被要求實作的介面。

    以 IList 為例,原型是這樣:

    interface IList: ICollection
    {
    }

    而 ICollection 的原型是這樣:

    interface ICollection : IEnumerable
    {
    }

    那麼就相當於:

    interface IList : ICollection, IEnumerable
    {
    }

    編譯器會自動把介面實作介面這件事直接展開到要實作的類別內,例如:

    public class ArrayList : IList { .. }

    會被展開為:

    public class ArrayList : IList, ICollection, IEnumerable {...}

    這和什麼介面擴充似乎一點關係也沒有,要說是明確實作也太過牽強了,頂多可說是另一種 compiler magic 吧。

    Reference: http://www.roxolan.com/2009/04/interface-inheritance-in-c.html


    學習不是查個 Google 套個書上的範例就算了,而是去熟悉了解每個程式碼背後的意義,否則就算學個幾百年,它也不會是你的。

      • 小朱的技術隨手寫:http://www.dotblogs.com.tw/regionbbs/
      • 雲端學堂Facebook: http://www.facebook.com/studyazure

    2012年11月21日 上午 02:41
    版主

所有回覆

  • 是不是有什麼誤會呢?

    例如,少按了「左鍵-->實作介面」之類的?


    理直氣和,切記。

    http://blog.kkbruce.net

    2012年11月19日 上午 06:15
  • 謝謝KKBrue,已修正提問

    實作ICollection的確是會出現

    2012年11月19日 上午 06:29
  • 您好,

    請參考:Why was the interface IList defined as inheriting the interface IEnumerable twice?


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    • 已標示為解答 Hans Chen 2012年11月19日 上午 07:34
    • 已取消標示為解答 Hans Chen 2012年11月19日 下午 03:17
    2012年11月19日 上午 07:13
  • 您好,

    請參考:Why was the interface IList defined as inheriting the interface IEnumerable twice?


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    我覺得這個說法不通, 你覺得通的道理是在哪 ?

    當一個 Interface 繼承自 ICollection 和 IEnumerable 時, 然後我寫一個類別實做這個新的 Interface

    不論我把這類別產生的 Instance 轉型為 ICollection 或 IEnumerable, 它執行 GetEnumerator() 的結果都相同.

    而且 GetEnumerator() 方法也只能實做一次.

    另一個狀況是, 如果我今天做一個新的 Interface , 只繼承 ICollection ,然後建一個新的 Class 來實做這個 Interface,

    它的 GetEnumerator() 方法事實上實做的來源還是IEnumerable

    或許是我忽略了某些地方以致於混淆了概念, 我沒法認同這篇文章講的理由, 亂馬客可以說說你贊同的理由是什麼嗎 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2012年11月19日 上午 07:42
    版主
  • 以下是我閱讀後的理解

    針對某一介面定義只會進行一次實作,即使繼承的多介面中有因直接或間接的定義是重覆的

    所以拿IList來說,為何又要再繼承IEnumerable介面
    原因是為了區隔IList介面想要自行處理,而非依靠ICollection介面中有關IEnumerable的定義
    簡單說就是將IEnumerable於不同介面的定義作區隔,實際上運作並沒有影響的意思
    2012年11月19日 上午 08:01
  • 如果ICollection有刻意隱藏IEnumerable的方法( new 關鍵字),
    然後在實現的class裡面去明確實作兩個介面的同名方法,那才有意義。

    但我覺得這樣違反了設計邏輯...介面與介面間的繼承應該叫"擴充於",在介面中隱藏母項的合約是很不自然的行為。
    這個案例中好像也不是這樣的情況。

    說真的,要我猜的話,我覺得原因是:
    讀程式碼或MSDN或SDK手冊時,明確好讀
    看到IList的同時,因為有寫出來,就馬上知道他實現了IEnumerable,而不用再去查ICollection是否擴充於IEnumerable介面。


    • 已編輯 Litfal 2012年11月19日 上午 08:09
    2012年11月19日 上午 08:04
  • 您好,
    我的想法覺得Interface是一個合約擴充,而非Class的繼承。

    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    2012年11月19日 上午 08:31
  • 您好,
    我的想法覺得Interface是一個合約擴充,而非Class的繼承。

    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/


    不論合約擴不擴充, 它的子系用的依然是 IEnumerable 的 GetEnumerator()  ,  我會寫 Class 的原因只是確認出兩個方式實做出來的東西會一樣.

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2012年11月19日 上午 08:45
    版主
  • 我和忠成討論了一下覺得這可能是某些因素造成的:

    (1) 一個可能原因是: C# 編譯器在 IL Code 中會將所有Interface 展開, 所以它文件乾脆也攤平寫.

    (2) 一個證據是 ArrayList 的 Source Code 中其實它是這樣宣告的

      public class ArrayList : IList, ICloneable

    但是在文件庫上的寫法也是整個攤開變成

      public class ArrayList : IList, ICollection, IEnumerable, ICloneable

    (3) 所以當你從 IL Code 反解回原始碼, 它就順勢被攤平. 所以我做了一個實驗

    我先寫一個類別庫的專案, 裡面寫了一個 Interface

    public interface IOnlyCollection:ICollection
    
    	{
    
    	}

    然後我將這個類別庫編譯完成後, 用 Reflector 去開啟這個 DLL, 然後看到的 Code 是

    public interface IOnlyCollection : ICollection, IEnumerable
    {
    }
    
     
    所以單純繼承 ICollection 和 同時繼承 ICollection 與 IEnumerable 意義上根本是一樣的 .

    註: 我另外發現這情形在 Visual Basic 的編譯器上似乎不會有相同的情況.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。



    • 已提議為解答 KKBruceMVP 2012年11月20日 上午 01:01
    • 已標示為解答 Hans Chen 2012年11月20日 上午 07:04
    • 已編輯 Bill ChungMVP, Moderator 2012年11月21日 上午 03:43 Reflector 誤植為 Reflection
    2012年11月19日 上午 11:13
    版主
  • 多謝各位大大的回覆
    2012年11月20日 上午 07:05
  • 以下是小弟的淺見,
    以IList:ICollection, IEnumerable來看
    表示如果您實作IList的話,就要實作IList, ICollection及IEnumerable的interface。

    所以從interface的實作來看,IList:ICollection, IEnumerable就不覺得奇怪了。

    MSDN文件中說的並不是IList還要繼承IEnumerable介面,而是說要讓實作的Class知道他要實作那些介面,或許文件是用程式產生出來的,所以讓人有IList還要繼承IEnumerable介面的誤解。

    所以在Class實作IList時,會有2個實作選項,實作介面'IList'(I) 及 明確實作介面'IList'(X)。

    如下圖所示。


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/


    • 已編輯 亂馬客 2012年11月22日 上午 10:36
    2012年11月21日 上午 01:31
  • 依照我的認知,介面實作介面的用意並不是要求介面實作,而是要求實作此介面的類別,必須要同時符合原介面以及介面被要求實作的介面。

    以 IList 為例,原型是這樣:

    interface IList: ICollection
    {
    }

    而 ICollection 的原型是這樣:

    interface ICollection : IEnumerable
    {
    }

    那麼就相當於:

    interface IList : ICollection, IEnumerable
    {
    }

    編譯器會自動把介面實作介面這件事直接展開到要實作的類別內,例如:

    public class ArrayList : IList { .. }

    會被展開為:

    public class ArrayList : IList, ICollection, IEnumerable {...}

    這和什麼介面擴充似乎一點關係也沒有,要說是明確實作也太過牽強了,頂多可說是另一種 compiler magic 吧。

    Reference: http://www.roxolan.com/2009/04/interface-inheritance-in-c.html


    學習不是查個 Google 套個書上的範例就算了,而是去熟悉了解每個程式碼背後的意義,否則就算學個幾百年,它也不會是你的。

      • 小朱的技術隨手寫:http://www.dotblogs.com.tw/regionbbs/
      • 雲端學堂Facebook: http://www.facebook.com/studyazure

    2012年11月21日 上午 02:41
    版主
  • 以下是來自 .NET Framework Reference Source Code 中的 ArrayList 宣告:

    public class ArrayList : IList, ICloneable
    {
        ...
    }

    證實了我上面的論點。


    學習不是查個 Google 套個書上的範例就算了,而是去熟悉了解每個程式碼背後的意義,否則就算學個幾百年,它也不會是你的。

      • 小朱的技術隨手寫:http://www.dotblogs.com.tw/regionbbs/
      • 雲端學堂Facebook: http://www.facebook.com/studyazure

    2012年11月21日 上午 03:28
    版主