那些年搞不懂的"協變"和"逆變"


  博主之前也不是很清楚協變與逆變,今天在書上看到了有關於協變還是逆變的介紹感覺還是不太懂,后來看了一篇園子里面一位朋友的文章,頓時茅塞頓開。本文里面會有自己的一些見解也會引用博友的一些正文,希望通過本篇,能讓大家對協變與逆變不再陌生。

What's 協變逆變?

  從字面理解協變就是"妥協的變化",而逆變則是"逆天的變化",哈哈,並不標准,我們來看看MSDN的解釋:  

    “協變”是指能夠使用與原始指定的派生類型相比,派生程度更大的類型。

    “逆變”則是指能夠使用派生程度更小的類型。

  解釋的很正確,大致就是這樣,不過不夠直白。通俗來講,協變就是從小到大,逆變則是從大到小。。協變逆變只會出現在泛型類和委托中,下面我們通過例子來了解協變和逆變。

  

  按照我們的理解,Dog繼承自Animal,所以Animal aAnimal = aDog; aDog 編輯器會隱式的轉變為Animal。但是List<Dog> 不繼承List<Animal> ,它們的身份是相等的,所以轉換失敗。

  如果想要轉換的話我們可以使用以下的代碼:

  List<Animal> animals= dogs.Select(c=>(Animal)c).ToList();

  這種寫法太麻煩了,有沒有一種方式可以直接賦值呢?答案是可以的,微軟為了幫助我們開發人員簡化這種寫法,幫我們定義了Out和in兩個關鍵字,我們看下官方對這兩個關鍵字的解釋:

  

  

  Out關鍵字可以簡單的定義為返回值參數,只能作為返回值。

  In關鍵字則定義為輸入型參數,其含義指In對應的類型在泛型成員中不能作為返回值。

  和剛開始說的一樣,T 用out 標記,所以T代表了輸出,也就是只能作為結果返回。

  

  因為T只能做結果返回,所以T不會被修改,編譯器就可以推斷下面的語句強制轉換合法,所以一下代碼編譯通過

    IEnumerable<Animal> animals = dogs;  

 

  再看逆變,在Main函數中添加:

  1. Action<AnimalactionAnimal = new Action<Animal>(a => {/*讓動物叫*/ });  
  2. Action<DogactionDog = actionAnimal;  
  3. actionDog(aDog); 

  很明顯actionAnimal 是讓動物叫,因為Dog是Animal,那么既然Animal 都能叫,Dog肯定也能叫。

  In 關鍵字:逆變,代表輸入,代表着只能被使用,不能作為返回值,所以C#編譯器可以根據in關鍵字推斷這個泛型類型只能被使用,所以Action<Dog> actionDog = actionAnimal;可以通過編譯器的檢查。

  再次演示Out關鍵字:添加兩個類:

  

  out關鍵字在上圖中只用於返回值,所以編輯器不會報錯。我們再來修改一下類

  

  如果泛型參數標記為out,泛型類成員參數又定義了T類型的話則編譯不通過。同樣的方法我們來測試下逆變:

  

 

總結

  Out:代表協變,只能當返回值類型使用,不能作為方法實參

  In:只能用作方法實參,不能用作返回值類型。

  我覺得只要上面兩個概念搞懂了,在自己敲下Demo就沒有問題了。

 

本文參考:http://www.cnblogs.com/majiang/articles/2607990.html

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM