C#中協(xié)變逆變的實(shí)現(xiàn)
1. 協(xié)變與逆變的概念
協(xié)變(Covariance)
允許將子類(派生類)類型作為父類(基類)類型使用。例如:IEnumerable<string> 可以被視為 IEnumerable<object>,因?yàn)?nbsp;string 是 object 的子類。按照前面我們?cè)诶^承中學(xué)習(xí)的里氏替換原則,父類裝子類,是不是就非常的合理,因?yàn)橐欢〞?huì)確保類型安全嘛。逆變(Contravariance)
允許將父類類型作為子類類型使用。例如:Action<object> 可以被視為 Action<string>,因?yàn)?nbsp;object 是 string 的父類。你看,居然父類可以變成子類,你想想,你的父親有一天居然可以化形為你的兒子來使用,是不是就很反常識(shí),不過這種轉(zhuǎn)化肯定是有限制滴,不然啥都可以轉(zhuǎn)化,是不是就整個(gè)面向?qū)ο缶蛠y成了一鍋粥。
核心思想:在類型安全的前提下,允許更靈活的泛型類型轉(zhuǎn)換。
2. 協(xié)變與逆變的作用及作用對(duì)象
作用
提 高泛型接口和委托的靈活性,使其能更自然地處理繼承關(guān)系。
減少強(qiáng)制 類型轉(zhuǎn)換,增強(qiáng)代碼的可讀性和安全性。
作用對(duì)象
- 泛型接口(如 IEnumerable<T>)
- 泛型委托(如 Func<T>、Action<T>)
- 不適用:類、結(jié)構(gòu)體或方法參數(shù)(僅支持接口和委托)。
請(qǐng)務(wù)必記住協(xié)變逆變的作用對(duì)象,僅僅只能在泛型接口和泛型委托中使用,在其他的地方是萬萬不行滴,不然就會(huì)天下大亂啦?。?!
3. 協(xié)變與逆變的關(guān)鍵字
out 關(guān)鍵字(協(xié)變)
標(biāo)記泛型類型參數(shù)為協(xié)變,表示該參數(shù)僅用于輸出位置(如返回值)。啥意思?就是說T這個(gè)泛型類型,在被out修飾了以后,他就只能被當(dāng)成返回值的類型了,不能當(dāng)做函數(shù)中傳參時(shí)候的類型了,為什么呢?因?yàn)槟憧傄鲂﹨^(qū)分嘛,要先列好規(guī)矩,互相才能正常運(yùn)行嘛,不然,你玩兒你的,我打我打的,整個(gè)繼承和多態(tài)就亂成了一鍋粥。
interface ICovariant<out T> { T GetItem(); }in 關(guān)鍵字(逆變)
標(biāo)記泛型類型參數(shù)為逆變,表示該參數(shù)僅用于輸入位置(如方法參數(shù))。啥意思?就是說T這個(gè)泛型類型,在被in修飾了以后,他就只能被當(dāng)成函數(shù)傳參時(shí)候的類型了,不能當(dāng)做返回值類型。
interface IContravariant<in T> { void Process(T item); }4. 泛型接口與委托的示例
示例1:協(xié)變?cè)诜盒徒涌谥械捏w現(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:逆變?cè)诜盒徒涌谥械捏w現(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é)變?cè)诜盒臀兄械捏w現(xiàn)
// 協(xié)變委托 delegate T Factory<out T>(); Factory<Dog> dogFactory = () => new Dog(); Factory<Animal> animalFactory = dogFactory; // 合法:Dog → Animal Animal animal = animalFactory();
示例4:逆變?cè)诜盒臀兄械捏w現(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)鍵字 | 方向 | 適用場(chǎng)景 |
|---|---|---|---|
| 協(xié)變 | out | 子類→父類 | 返回值、集合遍歷(如 IEnumerable<T>) |
| 逆變 | in | 父類→子類 | 方法參數(shù)、回調(diào)處理(如 Action<T>) |
協(xié)變:out修飾的泛型替代符,只能作為返回值,不能作為參數(shù)
逆變:in修飾的泛型替代符,只能作為參數(shù),不能作為返回值
協(xié)變:父類委托容器可以裝 子類泛型委托容器
逆變:子類委托容器可以裝 父類泛型委托容器
注意事項(xiàng):
- 協(xié)變類型參數(shù)必須僅用于返回值位置。
- 逆變類型參數(shù)必須僅用于方法參數(shù)位置。
- 不支持類的協(xié)變/逆變,僅限接口和委托。
到此這篇關(guān)于C#中協(xié)變逆變的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C# 協(xié)變逆變內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于C#中yield關(guān)鍵字的深入解析
這篇文章主要給大家介紹了關(guān)于C#中yield關(guān)鍵字的深入解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

