3 回答

TA貢獻1847條經(jīng)驗 獲得超7個贊
C#4.0中的協(xié)方差和協(xié)方差都涉及使用派生類而不是基類的能力。in / out關(guān)鍵字是編譯器提示,用于指示是否將類型參數(shù)用于輸入和輸出。
協(xié)方差
C#4.0中的協(xié)方差由out關(guān)鍵字輔助,這意味著使用outtype參數(shù)的派生類的泛型類型是可以的。因此
IEnumerable<Fruit> fruit = new List<Apple>();
由于Apple是Fruit,List<Apple>可以安全地用作IEnumerable<Fruit>
逆差
矛盾是in關(guān)鍵字,它表示輸入類型,通常在委托中。原理是相同的,這意味著委托可以接受更多派生類。
public delegate void Func<in T>(T param);
這意味著如果我們有一個Func<Fruit>,可以將其轉(zhuǎn)換為Func<Apple>。
Func<Fruit> fruitFunc = (fruit)=>{};
Func<Apple> appleFunc = fruitFunc;
如果它們基本上是相同的東西,為什么將它們稱為協(xié)/逆方差?
因為即使原理相同,也可以安全地從派生類型轉(zhuǎn)換為基類,但在輸入類型上使用時,我們可以安全地將較少派生類型(Func<Fruit>)強制轉(zhuǎn)換為更多派生類型(Func<Apple>),這很有意義,因為任何需要Fruit,也可以服用Apple。

TA貢獻1875條經(jīng)驗 獲得超3個贊
讓我分享我對這個話題的看法。
免責聲明:忽略空分配,我使用它們來使代碼保持相對簡短,并且它們足以查看編譯器想要告訴我們什么。
讓我們從類的層次結(jié)構(gòu)開始:
class Animal { }
class Mammal : Animal { }
class Dog : Mammal { }
現(xiàn)在定義一些接口,來說明什么in和out通用修飾符真正做到:
interface IInvariant<T>
{
T Get(); // ok, an invariant type can be both put into and returned
void Set(T t); // ok, an invariant type can be both put into and returned
}
interface IContravariant<in T>
{
//T Get(); // compilation error, cannot return a contravariant type
void Set(T t); // ok, a contravariant type can only be **put into** our class (hence "in")
}
interface ICovariant<out T>
{
T Get(); // ok, a covariant type can only be **returned** from our class (hence "out")
//void Set(T t); // compilation error, cannot put a covariant type into our class
}
好的,如果對接口in和out修飾符有限制,為什么還要麻煩使用它們呢?讓我們來看看:
不變性
讓我們從不變性開始(沒有in,沒有out修飾符)
不變性實驗
考慮 IInvariant<Mammal>
IInvariant<Mammal>.Get() -返回哺乳動物
IInvariant<Mammal>.Set(Mammal) -接受哺乳動物
如果我們嘗試:IInvariant<Mammal> invariantMammal = (IInvariant<Animal>)null?
呼叫者都IInvariant<Mammal>.Get()希望有哺乳動物,但是IInvariant<Animal>.Get()-會返回動物。并不是每個動物都是哺乳動物,所以它是不兼容的。
不管誰打電話,都IInvariant<Mammal>.Set(Mammal)希望哺乳動物能夠通過。由于IInvariant<Animal>.Set(Animal)可以接受任何動物(包括哺乳動物),因此兼容
結(jié)論:這種分配是不相容的
而如果我們嘗試:IInvariant<Mammal> invariantMammal = (IInvariant<Dog>)null?
呼叫者IInvariant<Mammal>.Get()期望有哺乳動物,IInvariant<Dog>.Get()-會返回一只狗,每只狗都是一只哺乳動物,因此是兼容的。
不管誰打電話,都IInvariant<Mammal>.Set(Mammal)希望哺乳動物能夠通過。由于IInvariant<Dog>.Set(Dog)接受只狗(而不是每個哺乳動物如狗),這是不兼容的。
結(jié)論:這種分配是不相容的
讓我們檢查是否正確
IInvariant<Animal> invariantAnimal1 = (IInvariant<Animal>)null; // ok
IInvariant<Animal> invariantAnimal2 = (IInvariant<Mammal>)null; // compilation error
IInvariant<Animal> invariantAnimal3 = (IInvariant<Dog>)null; // compilation error
IInvariant<Mammal> invariantMammal1 = (IInvariant<Animal>)null; // compilation error
IInvariant<Mammal> invariantMammal2 = (IInvariant<Mammal>)null; // ok
IInvariant<Mammal> invariantMammal3 = (IInvariant<Dog>)null; // compilation error
IInvariant<Dog> invariantDog1 = (IInvariant<Animal>)null; // compilation error
IInvariant<Dog> invariantDog2 = (IInvariant<Mammal>)null; // compilation error
IInvariant<Dog> invariantDog3 = (IInvariant<Dog>)null; // ok
這一點很重要:值得注意的是,根據(jù)泛型類型參數(shù)在類層次結(jié)構(gòu)中是較高還是較低,泛型類型本身由于不同的原因而不兼容。
好的,讓我們找出如何利用它。
協(xié)方差(out)
使用out通用修飾符時,您將具有協(xié)方差(請參見上文)
如果我們的類型看起來像:ICovariant<Mammal>,則聲明兩件事:
我的一些方法返回了哺乳動物(因此是out通用修飾符)-這很無聊
我的方法都不接受哺乳動物-盡管這很有趣,因為這是通用修飾符施加的實際限制out
我們?nèi)绾螐膐ut修飾符限制中受益?回顧上面的“不變性實驗”的結(jié)果?,F(xiàn)在嘗試看看對協(xié)方差進行相同的實驗會發(fā)生什么?
協(xié)方差實驗
如果我們嘗試:ICovariant<Mammal> covariantMammal = (ICovariant<Animal>)null?
呼叫者都ICovariant<Mammal>.Get()希望有哺乳動物,但是ICovariant<Animal>.Get()-會返回動物。并不是每個動物都是哺乳動物,所以它是不兼容的。
ICovariant.Set(Mammal) -由于out修改器的限制,這不再是一個問題!
結(jié)論這樣的分配是不相容的
而如果我們嘗試:ICovariant<Mammal> covariantMammal = (ICovariant<Dog>)null?
呼叫者ICovariant<Mammal>.Get()期望有哺乳動物,ICovariant<Dog>.Get()-會返回一只狗,每只狗都是一只哺乳動物,因此是兼容的。
ICovariant.Set(Mammal) -由于out修改器的限制,這不再是一個問題!
結(jié)論這樣的分配是兼容的
讓我們用代碼進行確認:
ICovariant<Animal> covariantAnimal1 = (ICovariant<Animal>)null; // ok
ICovariant<Animal> covariantAnimal2 = (ICovariant<Mammal>)null; // ok!!!
ICovariant<Animal> covariantAnimal3 = (ICovariant<Dog>)null; // ok!!!
ICovariant<Mammal> covariantMammal1 = (ICovariant<Animal>)null; // compilation error
ICovariant<Mammal> covariantMammal2 = (ICovariant<Mammal>)null; // ok
ICovariant<Mammal> covariantMammal3 = (ICovariant<Dog>)null; // ok!!!
ICovariant<Dog> covariantDog1 = (ICovariant<Animal>)null; // compilation error
ICovariant<Dog> covariantDog2 = (ICovariant<Mammal>)null; // compilation error
ICovariant<Dog> covariantDog3 = (ICovariant<Dog>)null; // ok
協(xié)方差(in)
使用in泛型修飾符時,您會產(chǎn)生矛盾(請參見上文)
如果我們的類型看起來像:IContravariant<Mammal>,則聲明兩件事:
我的一些方法接受哺乳動物(因此in通用修飾符)-這很無聊
我的方法都沒有返回哺乳動物-盡管這很有趣,因為這是通用修飾符施加的實際限制in
協(xié)方差實驗
如果我們嘗試:IContravariant<Mammal> contravariantMammal = (IContravariant<Animal>)null?
IContravariant<Mammal>.Get()-由于in修改器的限制,這不再是問題!
不管誰打電話,都IContravariant<Mammal>.Set(Mammal)希望哺乳動物能夠通過。由于IContravariant<Animal>.Set(Animal)可以接受任何動物(包括哺乳動物),因此兼容
結(jié)論:這樣的分配是兼容的
而如果我們嘗試:IContravariant<Mammal> contravariantMammal = (IContravariant<Dog>)null?
IContravariant<Mammal>.Get()-由于in修改器的限制,這不再是問題!
不管誰打電話,都IContravariant<Mammal>.Set(Mammal)希望哺乳動物能夠通過。由于IContravariant<Dog>.Set(Dog)接受只狗(而不是每個哺乳動物如狗),這是不兼容的。
結(jié)論:這種分配是不相容的
讓我們用代碼進行確認:
IContravariant<Animal> contravariantAnimal1 = (IContravariant<Animal>)null; // ok
IContravariant<Animal> contravariantAnimal2 = (IContravariant<Mammal>)null; // compilation error
IContravariant<Animal> contravariantAnimal3 = (IContravariant<Dog>)null; // compilation error
IContravariant<Mammal> contravariantMammal1 = (IContravariant<Animal>)null; // ok!!!
IContravariant<Mammal> contravariantMammal2 = (IContravariant<Mammal>)null; // ok
IContravariant<Mammal> contravariantMammal3 = (IContravariant<Dog>)null; // compilation error
IContravariant<Dog> contravariantDog1 = (IContravariant<Animal>)null; // ok!!!
IContravariant<Dog> contravariantDog2 = (IContravariant<Mammal>)null; // ok!!!
IContravariant<Dog> contravariantDog3 = (IContravariant<Dog>)null; // ok
順便說一句,這有點違反直覺,不是嗎?
// obvious
Animal animal = (Dog)null; // ok
Dog dog = (Animal)null; // compilation error, not every Animal is a Dog
// but this looks like the other way around
IContravariant<Animal> contravariantAnimal = (IContravariant<Dog>) null; // compilation error
IContravariant<Dog> contravariantDog = (IContravariant<Animal>) null; // ok
為什么不兼得?
因此,我們可以同時使用in和out一般的修飾?-顯然不是。
為什么?回顧一下限制in和out修飾符施加的限制。如果我們想使我們的泛型類型參數(shù)既協(xié)變又是協(xié)變的,我們基本上可以說:
接口的所有方法均未返回 T
我們接口的方法均不接受 T
這實質(zhì)上會使我們的通用接口成為非通用接口。
如何記住它?
你可以用我的把戲:)
“協(xié)變量”比“ contravaraint”短,這與它們的修飾符的長度相反(分別為“ out”和“ in”)
相反 varaint有點反直觀(請參見上面的示例)
- 3 回答
- 0 關(guān)注
- 364 瀏覽
添加回答
舉報