C#中協(xié)變逆變的實現(xiàn)
1. 協(xié)變與逆變的概念
協(xié)變(Covariance)
允許將子類(派生類)類型作為父類(基類)類型使用。例如:IEnumerable<string> 可以被視為 IEnumerable<object>,因為 string 是 object 的子類。按照前面我們在繼承中學(xué)習(xí)的里氏替換原則,父類裝子類,是不是就非常的合理,因為一定會確保類型安全嘛。逆變(Contravariance)
允許將父類類型作為子類類型使用。例如:Action<object> 可以被視為 Action<string>,因為 object 是 string 的父類。你看,居然父類可以變成子類,你想想,你的父親有一天居然可以化形為你的兒子來使用,是不是就很反常識,不過這種轉(zhuǎn)化肯定是有限制滴,不然啥都可以轉(zhuǎn)化,是不是就整個面向?qū)ο缶蛠y成了一鍋粥。
核心思想:在類型安全的前提下,允許更靈活的泛型類型轉(zhuǎn)換。
2. 協(xié)變與逆變的作用及作用對象
作用
提 高泛型接口和委托的靈活性,使其能更自然地處理繼承關(guān)系。
減少強制 類型轉(zhuǎn)換,增強代碼的可讀性和安全性。
作用對象
- 泛型接口(如 IEnumerable<T>)
- 泛型委托(如 Func<T>、Action<T>)
- 不適用:類、結(jié)構(gòu)體或方法參數(shù)(僅支持接口和委托)。
請務(wù)必記住協(xié)變逆變的作用對象,僅僅只能在泛型接口和泛型委托中使用,在其他的地方是萬萬不行滴,不然就會天下大亂啦?。。?nbsp;
3. 協(xié)變與逆變的關(guān)鍵字
out 關(guān)鍵字(協(xié)變)
標記泛型類型參數(shù)為協(xié)變,表示該參數(shù)僅用于輸出位置(如返回值)。啥意思?就是說T這個泛型類型,在被out修飾了以后,他就只能被當成返回值的類型了,不能當做函數(shù)中傳參時候的類型了,為什么呢?因為你總要做些區(qū)分嘛,要先列好規(guī)矩,互相才能正常運行嘛,不然,你玩兒你的,我打我打的,整個繼承和多態(tài)就亂成了一鍋粥。
interface ICovariant<out T> { T GetItem(); }in 關(guān)鍵字(逆變)
標記泛型類型參數(shù)為逆變,表示該參數(shù)僅用于輸入位置(如方法參數(shù))。啥意思?就是說T這個泛型類型,在被in修飾了以后,他就只能被當成函數(shù)傳參時候的類型了,不能當做返回值類型。
interface IContravariant<in T> { void Process(T item); }4. 泛型接口與委托的示例
示例1:協(xié)變在泛型接口中的體現(xiàn)
// 協(xié)變接口定義
interface IAnimal<out T>
{
T GetAnimal();
}
class Animal { }
class Dog : Animal { }
class AnimalShelter : IAnimal<Animal>
{
public Animal GetAnimal() => new Animal();
}
class DogShelter : IAnimal<Dog>
{
public Dog GetAnimal() => new Dog();
}
// 使用協(xié)變
IAnimal<Animal> shelter = new DogShelter(); // 合法:DogShelter → AnimalShelter
Animal animal = shelter.GetAnimal(); // 安全獲取Animal類型示例2:逆變在泛型接口中的體現(xiàn)
// 逆變接口定義
interface IFeeder<in T>
{
void Feed(T animal);
}
class Animal { }
class Dog : Animal { }
class AnimalFeeder : IFeeder<Animal>
{
public void Feed(Animal animal) => Console.WriteLine("Feeding animal");
}
class DogFeeder : IFeeder<Dog>
{
public void Feed(Dog dog) => Console.WriteLine("Feeding dog");
}
// 使用逆變
IFeeder<Dog> feeder = new AnimalFeeder(); // 合法:AnimalFeeder → DogFeeder
feeder.Feed(new Dog()); // 安全傳遞Dog類型給需要Animal的方法
//打印Feeding animal示例3:協(xié)變在泛型委托中的體現(xiàn)
// 協(xié)變委托 delegate T Factory<out T>(); Factory<Dog> dogFactory = () => new Dog(); Factory<Animal> animalFactory = dogFactory; // 合法:Dog → Animal Animal animal = animalFactory();
示例4:逆變在泛型委托中的體現(xiàn)
// 逆變委托
delegate void Handler<in T>(T obj);
Handler<Animal> animalHandler = (Animal a) => Console.WriteLine("Handle animal");
Handler<Dog> dogHandler = animalHandler; // 合法:Animal → Dog
dogHandler(new Dog()); // 安全調(diào)用總結(jié)
| 特性 | 關(guān)鍵字 | 方向 | 適用場景 |
|---|---|---|---|
| 協(xié)變 | out | 子類→父類 | 返回值、集合遍歷(如 IEnumerable<T>) |
| 逆變 | in | 父類→子類 | 方法參數(shù)、回調(diào)處理(如 Action<T>) |
協(xié)變:out修飾的泛型替代符,只能作為返回值,不能作為參數(shù)
逆變:in修飾的泛型替代符,只能作為參數(shù),不能作為返回值
協(xié)變:父類委托容器可以裝 子類泛型委托容器
逆變:子類委托容器可以裝 父類泛型委托容器
注意事項:
- 協(xié)變類型參數(shù)必須僅用于返回值位置。
- 逆變類型參數(shù)必須僅用于方法參數(shù)位置。
- 不支持類的協(xié)變/逆變,僅限接口和委托。
到此這篇關(guān)于C#中協(xié)變逆變的實現(xiàn)的文章就介紹到這了,更多相關(guān)C# 協(xié)變逆變內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
WPF使用DrawingContext實現(xiàn)二維繪圖
這篇文章介紹了WPF使用DrawingContext實現(xiàn)二維繪圖的方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
c#之用戶定義的數(shù)據(jù)類型轉(zhuǎn)換介紹
c#允許定義自己的數(shù)據(jù)類型,這意味著需要某些工具支持在自己的數(shù)據(jù)類型間進行數(shù)據(jù)轉(zhuǎn)換。方法是把數(shù)據(jù)類型轉(zhuǎn)換定義為相關(guān)類的一個成員運算符,數(shù)據(jù)類型轉(zhuǎn)換必須聲明是隱式或者顯式,以說明怎么使用它2014-01-01

