C#泛型接口的協(xié)變和逆變
1、什么是協(xié)變、逆變?
假設(shè):TSub是TParent的子類(lèi)。
協(xié)變:如果一個(gè)泛型接口IFoo<T>,IFoo<TSub>可以轉(zhuǎn)換為IFoo<TParent>的話,我們稱這個(gè)過(guò)程為協(xié)變,IFoo支持對(duì)參數(shù)T的協(xié)變。
逆變:如果一個(gè)泛型接口IFoo<T>,IFoo<TParent>可以轉(zhuǎn)換為IFoo<TSub>的話,我們稱這個(gè)過(guò)程為逆變,IFoo支持對(duì)參數(shù)T的逆變。
2、為什么要有協(xié)變、逆變?
通常只有具備繼承關(guān)系的對(duì)象才可以發(fā)生隱式類(lèi)型轉(zhuǎn)換,如Base b=new sub()。
協(xié)變和逆變可以使得更多的類(lèi)型之間能夠?qū)崿F(xiàn)隱式類(lèi)型轉(zhuǎn)換、類(lèi)型安全性有了保障。
3、為什么泛型接口要引入?yún)f(xié)變、逆變?
基于以上原因的同時(shí)、許多接口僅僅將類(lèi)型參數(shù)用于參數(shù)或返回值。所以支持協(xié)變和逆變后泛型的使用上有了更大的靈活性
4、為什么支持協(xié)變的參數(shù)只能用于方法的返回值?支持逆變的參數(shù)只能用于方法參數(shù)?
“TParent不能安全轉(zhuǎn)換成TSub”,是這兩個(gè)問(wèn)題的共同原因。
我們定義一個(gè)接口IFoo。
interface IFoo<T>
{
void Method1(T param);
T Method2();
}我們看一下協(xié)變的過(guò)程:IFoo<TSub>轉(zhuǎn)換成IFoo<TParent>。
- Method1:將TSub替換成TParent,Method1顯然存在 TParent到TSub的轉(zhuǎn)換。
- Method2:返回值類(lèi)型從TSub換成了TParent,是類(lèi)型安全的。
所以支持協(xié)變的參數(shù)只能用在方法的返回值中。
再看一下逆變的過(guò)程:IFoo<TParent>轉(zhuǎn)換成IFoo<TSub>。
- Method1:將TParent替換成TSub,Method1存在 TSub到TParent的轉(zhuǎn)換,是類(lèi)型安全的。
- Method2:返回值類(lèi)型從TParent換成了TSub,是不安全的。
所以支持逆變的參數(shù)只能用在方法的參數(shù)中。
5、泛型接口支持協(xié)變、逆變和不支持協(xié)變、逆變的對(duì)比?
這其實(shí)是對(duì)3個(gè)問(wèn)題的補(bǔ)充。
定義一個(gè)接口IFoo,既不支持協(xié)變,也不支持逆變。
interface IFoo<T>
{
void Method1(T param);
T Method2();
}實(shí)現(xiàn)接口IFoo
public class FooClass<T> : IFoo<T>
{
public void Method1(T param)
{
Console.WriteLine(default(T));
}
public T Method2()
{
return default(T);
}
}定義一個(gè)接口IBar支持對(duì)參數(shù)T的協(xié)變
interface IBar<out T>
{
T Method();
}實(shí)現(xiàn)接口IBar
public class BarClass<T> : IBar<T>
{
public T Method()
{
return default(T);
}
}定義一個(gè)接口IBaz支持對(duì)參數(shù)T的逆變
interface IBaz<in T>
{
void Method(T param);
}實(shí)現(xiàn)接口IBaz
public class BazClass<T> : IBaz<T>
{
public void Method(T param)
{
Console.WriteLine(param.ToString());
}
}定義兩個(gè)有繼承關(guān)系的類(lèi)型,IParent和SubClass。
interface IParent
{
void DoSomething();
}
public class SubClass : IParent
{
public void DoSomething()
{
Console.WriteLine("SubMethod");
}
}按照協(xié)變的邏輯,分別來(lái)使用IFoo和IBar。
//IFoo 不支持對(duì)參數(shù)T的協(xié)變
IFoo<SubClass> foo_sub = new FooClass<SubClass>();
IFoo<IParent> foo_parent = foo_sub;//編譯錯(cuò)誤
//IBar 支持對(duì)參數(shù)T的協(xié)變
IBar<SubClass> bar_sub = new BarClass<SubClass>();
IBar<IParent> bar_parent = bar_sub;foo_parent = foo_sub 會(huì)提示編譯時(shí)錯(cuò)誤“無(wú)法將類(lèi)型“IFoo<SubClass>”隱式轉(zhuǎn)換為“IFoo<IParent>”。存在一個(gè)顯式轉(zhuǎn)換(是否缺少?gòu)?qiáng)制轉(zhuǎn)換?)”
按照逆變的邏輯,分別來(lái)使用IFoo和IBaz。
//IFoo 對(duì)參數(shù)T逆變不相容
IFoo<IParent> foo_parent = null;
IFoo<SubClass> foo_sub = foo_parent;//編譯錯(cuò)誤
//IBaz 對(duì)參數(shù)T逆變相容
IBaz<IParent> baz_parent = null;
IBaz<SubClass> baz_sub = baz_parent;foo_sub = foo_parent 會(huì)提示編譯時(shí)錯(cuò)誤“無(wú)法將類(lèi)型“IFoo<IParent>”隱式轉(zhuǎn)換為“IFoo<ISub>”。存在一個(gè)顯式轉(zhuǎn)換(是否缺少?gòu)?qiáng)制轉(zhuǎn)換?)”
6、.NET4.0對(duì)IEnumerable接口的修改?
2.0中的定義:
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}4.0中的定義:
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}可以看到4.0中增加了對(duì)協(xié)變的支持。
可以在兩個(gè)版本試下, 下面的語(yǔ)句在2.0下會(huì)報(bào)錯(cuò)。
List<SubClass> subarr = new List<SubClass>();
IEnumerable<IParent> parentarr = subarr;以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#操作IIS程序池及站點(diǎn)的創(chuàng)建配置實(shí)現(xiàn)代碼
最近在做一個(gè)WEB程序的安裝包;對(duì)一些操作IIS進(jìn)行一個(gè)簡(jiǎn)單的總結(jié);主要包括對(duì)IIS進(jìn)行站點(diǎn)的新建以及新建站點(diǎn)的NET版本的選擇,還有針對(duì)IIS7程序池的托管模式以及版本的操作2013-03-03
C#開(kāi)發(fā)Windows窗體應(yīng)用程序的簡(jiǎn)單操作步驟
這篇文章主要介紹了C#開(kāi)發(fā)Windows窗體應(yīng)用程序的簡(jiǎn)單操作步驟,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
c# 成員類(lèi)型訪問(wèn)權(quán)限低于字段本身的實(shí)現(xiàn)
本文主要介紹了c# 成員類(lèi)型訪問(wèn)權(quán)限低于字段本身的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Unity OnGUI實(shí)時(shí)顯示游戲FPS
這篇文章主要為大家詳細(xì)介紹了Unity OnGUI實(shí)時(shí)顯示游戲FPS,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
C#數(shù)據(jù)結(jié)構(gòu)之字符串(string)詳解
這篇文章主要介紹了C#數(shù)據(jù)結(jié)構(gòu)之字符串(string),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
.Net WInform開(kāi)發(fā)筆記(二)Winform程序運(yùn)行結(jié)構(gòu)圖及TCP協(xié)議在Winform中的應(yīng)用
中午沒(méi)事,把去年剛畢業(yè)那會(huì)畫(huà)的幾張圖翻出來(lái)了,大概介紹Winform應(yīng)用程序運(yùn)行的過(guò)程,以及TCP協(xié)議在Winform中的應(yīng)用。感興趣的朋友可以了解下;如果有Windows消息機(jī)制等基礎(chǔ),很好理解這兩張2013-01-01
winform創(chuàng)建不規(guī)則窗體的方法
這篇文章主要介紹了winform創(chuàng)建不規(guī)則窗體的方法,涉及C#窗體創(chuàng)建的相關(guān)參數(shù)設(shè)置技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-09-09

