程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> c#筆記1-抗變與協變,

c#筆記1-抗變與協變,

編輯:C#入門知識

c#筆記1-抗變與協變,


前言

  工作一年了,平時也喜歡看看書,逛逛園子;但說到寫博,還真的沒有,說到底,只有一個字:懶!現在想改掉這個“毛病”了,希望多把平時工作學習到的知識和遇到的問題記錄下來,一是可以梳理自己的思路,加深理解;二是可以向更多的朋友學習和分享;三是可以鍛煉自己的寫作水平;可謂百利而無一害!

  平時偶爾會遇到一些小問題,很多時候都是查了記住,或者簡單寫寫筆記,當時理解就過了,沒有形成文檔,等過段時間又遇到同樣的問題,又要重新去查去理解,甚是麻煩。希望以後把這些東西寫成文章,盡管可能是很小的問題,也當做筆記記錄。關於c#的一些概念、語法或者規范,就記錄在【c#筆記】。由於不是初學邊學邊記,所以沒有一定的時間和學習順序,只是平時遇到覺得有必要,就記錄下來。

一、遇到問題

  工作是基於.net3.5開發,實際過程遇到一個問題。假設我們有一個 Base 類,一個 Derived 類,Derived 繼承了 Base。如下:

    class Base
    {

    }
    class Derived : Base
    {

    }  

  當我用IEnumerable<Base> 作為形參,List<Derived> 作為實參時,發現編譯出錯了!原本父類作為形參,傳遞子類是再正常不過的,但在泛型中確編譯不通過。

二、探究問題

  通常我們在設計參數和返回值都有一個原則,參數要盡可能“泛”,返回值要盡可能的“細”。泛,指得是用接口或者父類作為參數,這樣可以接收更多的參數類型;細,指的是返回具體類型,這樣可以更好說明方法的作用。

  舉個例子:

        string[] strs = new string[] { "hello", "word" };

        //這樣的缺點是數組就傳遞不了了,還要調用一次 ToList()
        static void Test_1(List<string> list)
        {

        }

        //正確的做法,應該用IEnumerable<T>
        static void Test_2(IEnumerable<string> list)
        {

        }  

  可見,參數的“泛”可以提供更大的靈活性。

  接著就進入本次的主題:抗變與協變。需要說明的是,抗變與協變是在4.0開始支持的。假設有一個方法需要Derived集合作為參數,那麼基於上面的原則,我們會這樣設計:

  static void TestIn(IEnumerable<Base> bases)
  {

  }

  接著我們向下面這樣調用,在3.5下就會發現編譯不通過,提示無法將 List<Derived>轉換為IEnumerable<Base>。

 List<Derived> listIn = new List<Derived>();
 TestIn(listIn);

  同樣的代碼,我們拿到4.0下,發現編譯通過了。比較 IEnumerable泛型接口,我們發現4.0下的定義為:  

 public interface IEnumerable<out T> : IEnumerable

  發現多了 out 關鍵字,這就是協變。msdn對於類型參數的解釋是:out T 要枚舉的對象的類型。該類型參數是協變的。即可以使用指定的類型或派生程度更高的類型。

  我們可以這樣理解協變,參數的類型就是協變的,父類用子類代替,也就是子類當父類使用。

  理解協變後,抗變就好理解了。函數的返回值就是抗變的,子類用父類代替,也就是父類當子類使用。在非泛型的情況下,我們可以這樣接收方法的返回值:  

  object obj = Test_3();

  static string Test_3()
  {
    return "hello world";
  }  

  當然,我們覺得這樣調用也應該是可以的:  

  IEnumerable<Base> listOut = TestOut();
  static IEnumerable<Derived> TestOut()
  {
    return new List<Derived>();
  }

  在3.5下,這樣同樣會編譯錯誤。4.0下就沒有問題。

三、總結

   協變與抗變的概念其實我們經常遇到(參數協變、返回值抗變),而且我們也會習慣的這樣設計。但對於泛型,.net 到了4.0才提供這樣的支持,這為泛型的使用提供了更大的靈活性。

  ok,實際我們不怎麼需要去理解概念性的東西,知道原理和理解怎麼使用即可。以上是我的個人理解,如果有朋友想要更深入的理解,可以參見msdn。

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