C# 協變與逆變
阿新 • • 發佈:2020-12-28
“協變”是指能夠使用與原始指定的派生型別相比,派生程度更大的型別。
“逆變”則是指能夠使用派生程度更小的型別。
直白的理解:
“協變”->”和諧的變”->”很自然的變化”->string->object :協變。
“逆變”->”逆常的變”->”不正常的變化”->object->string 逆變。
如:
// 支援協變的自定義介面 public interface IOutDemo<out T> { } public class OutDemo<T> : IOutDemo<T>{}// 支援逆變的自定義介面 public interface IInDemo<in T> { } public class InDemo<T> : IInDemo<T> { }
使用自定義的協變和逆變介面
// Dog : Animal 即 Dog是Animal的子類 OutDemo<Animal> outDemo =new OutDemo<Dog>();// 錯誤,只有泛型介面或者泛型委託可以使用協變 IOutDemo<Animal> _outDemo= newOutDemo<Dog>(); // 正確,泛型介面使用協變,Dog->Animal InDemo<Dog> inDemo = new InDemo<Animal>();// 錯誤,只有泛型介面或者泛型委託可以使用逆變 IInDemo<Dog> _inDemo = new InDemo<Animal>(); //正確,,泛型介面使用逆變,Animal->Dog
C#協變和逆變只能用在泛型介面和泛型委託上
協變(out)用在方法的返回值型別上,逆變(in)用在方法的引數型別上
publicinterface IMyList<in T,out V> { // out 用於協變,限制該泛型介面引數只能作為介面方法的返回值型別 V Get(T x); // in 用於逆變,限制該泛型介面引數只能作為介面方法的引數型別 void Set(T x); //T Test(T x); // 錯誤, in T 限制了T只能作為方法引數型別 //V Test(V x); // 錯誤, out V 限制了V只能作為方法返回值型別 }
個人理解:由於out限制了引數只能作為方法的返回值型別,則子類協變為父類就可以安全轉換(因為總是可以隱式的將子類的例項賦值給父型別的變數);
同樣由於in限制了引數只能作為方法的引數型別,意味著只能使用該引數,當把父類逆變為子類進行使用,由於子類和父類存在繼承關係(即編譯器內部可以使用顯示強制轉換,把父型別轉換為子型別),子類繼承了父類的資源,意味著顯示轉換後,你要使用父類的資源子類都繼承了都具有該資源;
Dog dog = new Dog(); Animal animal = dog; // 總是可以將子類的例項賦值給父型別的變數(隱式轉換) Dog dog1 = (Dog)animal; // 需要顯示轉換(強制轉換),因為存在繼承關係所以可以強制轉換成功