C#中的協(xié)變與逆變方式
前言
在 C# 中,協(xié)變(Covariance)和逆變(Contravariance)是兩個(gè)重要的概念,主要用于處理泛型類型參數(shù)的可變性。
這兩個(gè)概念允許在泛型類型之間進(jìn)行更靈活的轉(zhuǎn)換,提高了代碼的可重用性和靈活性。
協(xié)變
(一)定義與概念
協(xié)變允許將一個(gè)派生類型的泛型參數(shù)轉(zhuǎn)換為基類型的泛型參數(shù)。
簡單來說,如果有兩個(gè)泛型接口IFoo和IBar,并且IFoo的泛型參數(shù)T是協(xié)變的,那么如果A是B的派生類,就可以將IFoo轉(zhuǎn)換為IFoo。
(二)使用場景與示例
示例一:泛型接口中的協(xié)變
interface IFoo<out T>
{
T GetValue();
}假設(shè)我們有以下接口定義:
這里的out關(guān)鍵字表示T是協(xié)變的?,F(xiàn)在我們可以這樣使用這個(gè)接口:
class Animal {}
class Dog : Animal {}
class Program
{
static void Main()
{
IFoo<Dog> dogFoo = new DogFoo();
IFoo<Animal> animalFoo = dogFoo; // 協(xié)變轉(zhuǎn)換允許
Animal animal = animalFoo.GetValue();
}
}
class DogFoo : IFoo<Dog>
{
public Dog GetValue()
{
return new Dog();
}
}在這個(gè)例子中,由于IFoo的T是協(xié)變的,我們可以將IFoo轉(zhuǎn)換為IFoo。
這是因?yàn)镈og是Animal的派生類,并且GetValue方法只返回T類型的值,不會對其進(jìn)行修改,所以這種轉(zhuǎn)換是安全的。
示例二:委托中的協(xié)變
delegate T Func<out T>();
class Program
{
static void Main()
{
Func<Dog> dogFunc = GetDog;
Func<Animal> animalFunc = dogFunc; // 協(xié)變轉(zhuǎn)換允許
Animal animal = animalFunc();
}
static Dog GetDog()
{
return new Dog();
}
}C# 中的委托也可以支持協(xié)變。
例如:
這里的委托Func<out T>的T是協(xié)變的,所以可以將Func<Dog>轉(zhuǎn)換為Func<Animal>。
逆變
(一)定義與概念
逆變允許將一個(gè)基類型的泛型參數(shù)轉(zhuǎn)換為派生類型的泛型參數(shù)。
如果有兩個(gè)泛型接口IFoo<T>和IBar<T>,并且IFoo<T>的泛型參數(shù)T是逆變的,那么如果A是B的派生類,就可以將IFoo<B>轉(zhuǎn)換為IFoo<A>。
(二)使用場景與示例
示例一:泛型接口中的逆變
考慮以下接口定義:
interface IBar<in T>
{
void SetValue(T value);
}這里的in關(guān)鍵字表示T是逆變的。
現(xiàn)在我們可以這樣使用這個(gè)接口:
class Animal {}
class Dog : Animal {}
class Program
{
static void Main()
{
IBar<Animal> animalBar = new AnimalBar();
IBar<Dog> dogBar = animalBar; // 逆變轉(zhuǎn)換允許
dogBar.SetValue(new Dog());
}
}
class AnimalBar : IBar<Animal>
{
public void SetValue(Animal value) {}
}在這個(gè)例子中,由于IBar的T是逆變的,我們可以將IBar轉(zhuǎn)換為IBar。
這是因?yàn)镾etValue方法接受一個(gè)T類型的參數(shù),并且Dog是Animal的派生類,所以可以將Dog類型的參數(shù)傳遞給接受Animal類型參數(shù)的方法,這種轉(zhuǎn)換是安全的。
示例二:委托中的逆變
delegate void Action<in T>(T value);
class Program
{
static void Main()
{
Action<Animal> animalAction = SetAnimal;
Action<Dog> dogAction = animalAction; // 逆變轉(zhuǎn)換允許
dogAction(new Dog());
}
static void SetAnimal(Animal value) {}
}委托也可以支持逆變。
例如:
這里的委托Action<in T>的T是逆變的,所以可以將Action<Animal>轉(zhuǎn)換為Action<Dog>。
協(xié)變與逆變的限制與注意事項(xiàng)
(一)泛型類型參數(shù)的限制
對于協(xié)變的泛型類型參數(shù),只能在接口或委托中作為返回值類型出現(xiàn),不能作為方法的參數(shù)類型。
例如,以下代碼是不合法的:
interface IFoo<out T>
{
void DoSomething(T value); // 錯(cuò)誤,協(xié)變類型不能作為參數(shù)
}對于逆變的泛型類型參數(shù),只能在接口或委托中作為方法的參數(shù)類型出現(xiàn),不能作為返回值類型。
例如,以下代碼是不合法的:
interface IBar<in T>
{
T GetValue(); // 錯(cuò)誤,逆變類型不能作為返回值
}(二)數(shù)組的協(xié)變與逆變
C#中的數(shù)組在一定程度上支持協(xié)變,但這種協(xié)變是不安全的。
例如:
Animal[] animals = new Dog[10]; // 編譯通過,但運(yùn)行時(shí)可能會拋出異常 animals[0] = new Cat(); // 如果這里是一個(gè) Cat 對象,運(yùn)行時(shí)會拋出異常
與數(shù)組不同,泛型類型的協(xié)變和逆變是安全的,因?yàn)榫幾g器會在編譯時(shí)進(jìn)行類型檢查,確保轉(zhuǎn)換的安全性。
(三)可變性的范圍
協(xié)變和逆變只適用于引用類型,對于值類型是不適用的。
例如,以下代碼是不合法的:
interface IFoo<out T> where T : struct // 錯(cuò)誤,協(xié)變不能用于值類型
{
T GetValue();
}總結(jié)
協(xié)變和逆變是 C#中強(qiáng)大的概念,可以提高代碼的靈活性和可重用性。
通過正確使用協(xié)變和逆變,可以在泛型類型之間進(jìn)行安全的轉(zhuǎn)換,使得代碼更加簡潔和易于維護(hù)。
然而,在使用協(xié)變和逆變時(shí),需要注意類型參數(shù)的限制和安全性,以避免潛在的運(yùn)行時(shí)錯(cuò)誤
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C#從文件或標(biāo)準(zhǔn)輸入設(shè)備讀取指定行的方法
這篇文章主要介紹了C#從文件或標(biāo)準(zhǔn)輸入設(shè)備讀取指定行的方法,涉及C#文件及IO操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
WinForm中comboBox控件數(shù)據(jù)綁定實(shí)現(xiàn)方法
這篇文章主要介紹了WinForm中comboBox控件數(shù)據(jù)綁定實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了WinForm實(shí)現(xiàn)comboBox控件數(shù)據(jù)綁定的常用方法與相關(guān)操作技巧,需要的朋友可以參考下2017-05-05
c# 獲取網(wǎng)頁中指定的字符串信息的實(shí)例代碼
c# 獲取網(wǎng)頁中指定的字符串信息的實(shí)例代碼,需要的朋友可以參考一下2013-04-04
WPF利用CommunityToolkit.Mvvm實(shí)現(xiàn)級聯(lián)選擇器
這篇文章主要介紹了WPF如何利用CommunityToolkit.Mvvm實(shí)現(xiàn)級聯(lián)選擇器,文中的示例代碼講解詳細(xì),對我們的學(xué)習(xí)或工作有一定幫助,需要的小伙伴可以參考一下2023-12-12
基于WPF簡單實(shí)現(xiàn)Meesage消息提醒
這篇文章主要介紹了如何利用WPF簡單實(shí)現(xiàn)Meesage消息提醒,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下2023-07-07

