1. 程式人生 > 實用技巧 >C# 協變與逆變

C# 協變與逆變

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

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

直白的理解:

“協變”->”和諧的變”->”很自然的變化”->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= new
OutDemo<Dog>(); // 正確,泛型介面使用協變,Dog->Animal InDemo<Dog> inDemo = new InDemo<Animal>();// 錯誤,只有泛型介面或者泛型委託可以使用逆變 IInDemo<Dog> _inDemo = new InDemo<Animal>(); //正確,,泛型介面使用逆變,Animal->Dog

C#協變和逆變只能用在泛型介面和泛型委託上

協變(out)用在方法的返回值型別上,逆變(in)用在方法的引數型別上

    public
interface 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;  // 需要顯示轉換(強制轉換),因為存在繼承關係所以可以強制轉換成功