1, 泛型接口的協變測試
若是泛型類型用out關鍵字標註,泛型接口就是協變的。這也意味着返回類型只能是T。spa
泛型接口的抗變code
若是泛型類型用in關鍵字標註,泛型接口就是抗變的。這樣,接口只能把泛型類型T用做其方法的輸入,即方法的參數。對象
這是泛型接口的抗變和協變的定義,那咱們下面來用代碼說明,直接上代碼,blog
1 /// <summary> 2 /// 泛型接口 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public interface IDisplay< T > 6 { 7 void Show(T item); 8 } 9 10 /// <summary> 11 /// 實現泛型接口IDisaplay 12 /// </summary> 13 /// <typeparam name="T"></typeparam> 14 public class ShapDisplay<T> : IDisplay<T> 15 { 16 public void Show(T item) 17 { 18 Console.WriteLine("測試成功!"); 19 } 20 } 21 22 /// <summary> 23 /// 父類 24 /// </summary> 25 public class ParentClass 26 { 27 } 28 29 /// <summary> 30 /// 子類 31 /// </summary> 32 public class SubClass : ParentClass 33 { 34 }
2, 上面定義了接口和實現了接口,接下來咱們來測試實現了接口的類,上代碼接口
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 用子類實例化泛型類(簡稱子類對象) 6 IDisplay<SubClass> sub1 = new ShapDisplay<SubClass>(); 7 8 // 用父類實例化泛型類(簡稱父類對象) 9 IDisplay<ParentClass> par1 = new ShapDisplay<ParentClass>(); 10 11 // 用父類類型接收子類對象(子類對象→父類類型)協變 12 IDisplay<ParentClass> parent = sub1; 13 14 // 用子類類型接收父類對象(父類對象→子類類型)抗變 15 IDisplay<SubClass> sub = par1; 16 17 Console.ReadKey(); 18 19 } 20 }
咱們會發現代碼行12和15會報錯,編譯不過,爲何呢?string
緣由很簡單,由於咱們在最上面是這樣定義接口的時候,沒有加out也沒有加in,即泛型接口默認不會支持抗變和協變,因此編譯會報錯。it
好,那咱們接下來給泛型接口修改一下,以下代碼編譯
1 /// <summary> 2 /// 泛型接口 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public interface IDisplay<out T> 6 { 7 void Show(T item); 8 }
泛型前面加上out以後,會發現接口中的Show會報錯,這又是爲什麼呢?class
根據泛型接口的協變,若是泛型類型用out關鍵字標註,這意味着返回類型只能是T。也就是說方法的返回類型應該是T,而咱們Show方法中,方法的參數是T,因此不符合規定,報錯。
那咱們再來修改代碼,以下
1 /// <summary> 2 /// 泛型接口 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public interface IDisplay<in T> 6 { 7 void Show(T item); 8 }
接口徹底沒問題,可是,囧,main方法中12行依然報錯,wtf?
由於泛型類型是用in來標註的,這表示該泛型只支持抗變,12行代碼是協變,因此會報錯。
到此,泛型接口的抗變和協變也就解釋完畢,總結以下3點,
①泛型接口,若是泛型類型前沒有關鍵字out或者in來標註,則該泛型接口不支持抗變和協變,即只能是什麼對象指向什麼類型。
②若是泛型接口,泛型類型前有關鍵字out標註,則表示其方法的輸出爲T類型,也就是方法的返回值。同時該泛型接口支持協變,即,能夠用父類的類型指向子類的對象。
③若是泛型接口,泛型類型前面有關鍵字in標註,則表示其方法的輸入爲T類型,也就是方法的參數。該泛型接口支持抗變,也就是能夠用子類的類型指向父類的對象。