根據可變性的規則,只有介面和委托可以標記可變性。且只有類型參數為引用類型時才可以利用可變性。 不變性:泛型類型的參數不能改變,這表示一個創建一個MyInterface<String>類型的對象時,賦值給它的只能是MyInterface<String>類型 逆變性:泛型的類型參數可以從一個類變成它的派 ...
根據可變性的規則,只有介面和委托可以標記可變性。且只有類型參數為引用類型時才可以利用可變性。
不變性:泛型類型的參數不能改變,這表示一個創建一個MyInterface<String>類型的對象時,賦值給它的只能是MyInterface<String>類型
1 namespace ContravariantAndCovariant 2 { 3 public interface MyInterface<T>//沒有指定任何可變性,說明這個介面的類型參數是不變的 4 { 5 void Show(T t); 6 } 7 8 public class ShowObject : MyInterface<Object> 9 { 10 public void Show(object t) 11 { 12 Console.WriteLine(t.ToString()); 13 } 14 } 15 16 public class ShowString : MyInterface<String> 17 { 18 public void Show(string t) 19 { 20 Console.WriteLine(t); 21 } 22 } 23 24 public class ShowInt : MyInterface<Int32> 25 { 26 public void Show(int t) 27 { 28 Console.WriteLine(t.ToString()); 29 } 30 } 31 32 class Program 33 { 34 static void Main(string[] args) 35 { 36 //正確 37 MyInterface<String> str_invariant = new ShowString();//只能賦值MyInterface<String>類型 38 //錯誤 39 MyInterface<String> str_contravariant = new ShowObject();//當賦值其它類型的時候,編譯器會報錯 40 //CS0266 無法將類型“ContravariantAndCovariant.ShowObject”隱式轉換為“ContravariantAndCovariant.MyInterface<string>”。存在一個顯式轉換(是否缺少強制轉換?) 41 42 } 43 } 44 45 }
逆變性:泛型的類型參數可以從一個類變成它的派生類,比如:可以把MyInterface<object>轉換成MyInterface<string>。逆變用in表示,因為它出現在輸入的位置(方法的參數),in 就是進去的意思。
1 namespace ContravariantAndCovariant 2 { 3 public interface MyInterface<in T>//用in關鍵字指定T的逆變性 4 { 5 void Show(T t);//T是參數類型 6 } 7 8 public class ShowObject : MyInterface<Object> 9 { 10 public void Show(object t) 11 { 12 Console.WriteLine(t.ToString()); 13 } 14 } 15 16 public class ShowString : MyInterface<String> 17 { 18 public void Show(string t) 19 { 20 Console.WriteLine(t); 21 } 22 } 23 24 public class ShowInt : MyInterface<Int32> 25 { 26 public void Show(int t) 27 { 28 Console.WriteLine(t.ToString()); 29 } 30 } 31 32 class Program 33 { 34 static void Main(string[] args) 35 { 36 MyInterface<object> obj = null; 37 MyInterface<string> str = obj;//可以把MyInterface<object>類型轉換為MyInterface<string>類型 38 } 39 } 40 41 }
協變性:泛型的類型參數可以從一個類變成它的基類,比如:可以把MyInterface<string>轉換成MyInterface<object>。逆變用out表示,因為它出現在輸出的位置(方法返回值),out 就是出的意思。
1 namespace ContravariantAndCovariant 2 { 3 public interface MyInterface<out T>//用out關鍵字指定T的協變性 4 { 5 T Show(); //T是返回類型 6 } 7 8 public class ShowObject : MyInterface<Object> 9 { 10 public object Show() 11 { 12 return null; 13 } 14 } 15 16 public class ShowString : MyInterface<String> 17 { 18 public string Show() 19 { 20 return null; 21 } 22 } 23 24 class Program 25 { 26 static void Main(string[] args) 27 { 28 //錯誤 29 //MyInterface<object> obj = null; 30 //MyInterface<string> str = obj;//不能把MyInterface<object>類型轉換為MyInterface<string>類型 31 32 //正確 33 MyInterface<string> str = null; 34 MyInterface<object> obj = str;//可以把MyInterface<string>類型轉換成MyInterface<Object>類型 35 } 36 } 37 }
CLR是要保證類型的轉換是安全的,協變性還好理解,子類是可以轉換成父類的,因為子類對象中包括父類對象的所有成員。這個轉換是安全的。
但是逆變的話,父類轉換成子類是不安全的,當不能里氏轉換的時候,父類對象中並沒有子類的所有成員。
其實這麼看的話是有一個誤區,就是關於引用和對象。當逆變的時候,比如上面逆變的代碼中:
MyInterface<object> obj = null;
MyInterface<string> str = obj;
str.Show("123")
str是MyInterface<string>類型的變數,但是它引用的對象是MyInterface<Object>類型的,所以當我們調用Show()方法的時候,其實是obj.Show();
obj的參數是object類型的,我們傳入的是一個string類型,string---->object的轉換是安全的。所以逆變是安全的!
同理的話,協變:
MyInterface<string> str = null;
Myinterface<object> obj = str;
object result = obj.show();
可以看到obj是MyInterface<object>類型的變數,但是它引用的對象是MyInterface<string>類型,所以當我們調用Show()方法的時候,其實是str.Show(),
它返回的是string類型,但是obj.Show()返回的是一個object類型,所以我們用一個object類型的變數result去接收返回值,最終變成 string ----->object類型的轉換,所以協變是安全的。