c# 泛型協變逆變學習
最近在項目開發當中使用泛型委托Func較多,查看Func的定義就會發現Func的入參都會都會標記上in,出參都會標記上out.
in 和out和泛型類型實參有關, 其中in代表逆變,out代表協變.自己協變和逆變在設計接口或者委托的時候也沒有定義過,
因此就詳細了解一下其用法.
一.關於協變和逆變
在c# 4.0後,泛型類型參數分為以下三種請情況
1.不變量 這帶便泛型參數類型參數不能更改
2.協變量 泛型類型參數可以從一個類更改為它的某個基類.c#中使用out關鍵字標記協變量形式的泛型類型參數.
協變量泛型類型參數只能出現在輸出位置,作為方法的返回類型
3.逆變量 泛型類型參數可以從一個類更改為它的某個派生類.c#中使用in關鍵字標記逆變量形式的泛型類型參數
逆變量泛型類型參數只能出現在輸入位置,作為方法的入參
比如在上述截圖的代碼中,T1和T2作為委托的入參,Tresult作為委托的出參
二 引入協變逆變
我們看如下代碼
class Program { static void Main(string[] args) { //1. { //List<A> list = new List<B>(); error List<B>並不是List<A>的子類View Code} //2. { IEnumerable<A> list = new List<B>(); // IEnumerable<B> list1 = new List<A>();// ERROR } //3. { Func<A> func = null; Func<B> func1 = null; func = func1; // func1 = func; ERROR } //4. { Action<A> action = null; Action<B> action1 = null; action1 = action; // action = action1; error } } } public interface A { } public class B : A { }
(1)其中第一個註釋部分很明顯示編譯不過的,因為前後關系並不是繼承關系.
(2)第二個編譯器能編譯通過,第2個不能編譯通過,
我們看下 IEnumerable接口的定義
public interface IEnumerable<out T> : IEnumerable
泛型類型前面標識為out
(3)第三個泛型委托中,第1個能編譯通過,第二個不能
我們看下Func的定義
public delegate TResult Func<out TResult>();
泛型類型前面標識為out
(4)第四個泛型委托中,第1個能編譯通過,第2個不能
我們看下Action的定義
public delegate void Action<in T>(T obj)
泛型類型前面標識為in
以上代碼展示了協變和逆變的基本特性
即協變時,泛型類型參數可以從一個類更改為它的某個基類,且該類型僅可作為出參
逆變時,泛型類型參數可以從一個類更改為它的某個派生類,且該類型僅可作為入參
總結
為什必須顯示使用in或則out標記類型類型參數,因為我們在編碼過程中,需要我們自己去定義協議,
明確告訴編譯器允許什麽,那麽在編譯器就能識別出你的泛型類型參數到底能存在什麽樣的位置上,
若果不用定這個協議,那麽編譯器在編譯時不能識別出,在運行時將會拋出未知的錯誤.
協變和逆變在設計框架時會有較大的幫助,後續會繼續分享在設計框架時協變逆變的用法.
c# 泛型協變逆變學習