C#泛型接口的協(xié)變和逆變
1、什么是協(xié)變、逆變?
假設:TSub是TParent的子類。
協(xié)變:如果一個泛型接口IFoo<T>,IFoo<TSub>可以轉(zhuǎn)換為IFoo<TParent>的話,我們稱這個過程為協(xié)變,IFoo支持對參數(shù)T的協(xié)變。
逆變:如果一個泛型接口IFoo<T>,IFoo<TParent>可以轉(zhuǎn)換為IFoo<TSub>的話,我們稱這個過程為逆變,IFoo支持對參數(shù)T的逆變。
2、為什么要有協(xié)變、逆變?
通常只有具備繼承關系的對象才可以發(fā)生隱式類型轉(zhuǎn)換,如Base b=new sub()。
協(xié)變和逆變可以使得更多的類型之間能夠?qū)崿F(xiàn)隱式類型轉(zhuǎn)換、類型安全性有了保障。
3、為什么泛型接口要引入?yún)f(xié)變、逆變?
基于以上原因的同時、許多接口僅僅將類型參數(shù)用于參數(shù)或返回值。所以支持協(xié)變和逆變后泛型的使用上有了更大的靈活性
4、為什么支持協(xié)變的參數(shù)只能用于方法的返回值?支持逆變的參數(shù)只能用于方法參數(shù)?
“TParent不能安全轉(zhuǎn)換成TSub”,是這兩個問題的共同原因。
我們定義一個接口IFoo。
interface IFoo<T>
{
void Method1(T param);
T Method2();
}我們看一下協(xié)變的過程:IFoo<TSub>轉(zhuǎn)換成IFoo<TParent>。
- Method1:將TSub替換成TParent,Method1顯然存在 TParent到TSub的轉(zhuǎn)換。
- Method2:返回值類型從TSub換成了TParent,是類型安全的。
所以支持協(xié)變的參數(shù)只能用在方法的返回值中。
再看一下逆變的過程:IFoo<TParent>轉(zhuǎn)換成IFoo<TSub>。
- Method1:將TParent替換成TSub,Method1存在 TSub到TParent的轉(zhuǎn)換,是類型安全的。
- Method2:返回值類型從TParent換成了TSub,是不安全的。
所以支持逆變的參數(shù)只能用在方法的參數(shù)中。
5、泛型接口支持協(xié)變、逆變和不支持協(xié)變、逆變的對比?
這其實是對3個問題的補充。
定義一個接口IFoo,既不支持協(xié)變,也不支持逆變。
interface IFoo<T>
{
void Method1(T param);
T Method2();
}實現(xiàn)接口IFoo
public class FooClass<T> : IFoo<T>
{
public void Method1(T param)
{
Console.WriteLine(default(T));
}
public T Method2()
{
return default(T);
}
}定義一個接口IBar支持對參數(shù)T的協(xié)變
interface IBar<out T>
{
T Method();
}實現(xiàn)接口IBar
public class BarClass<T> : IBar<T>
{
public T Method()
{
return default(T);
}
}定義一個接口IBaz支持對參數(shù)T的逆變
interface IBaz<in T>
{
void Method(T param);
}實現(xiàn)接口IBaz
public class BazClass<T> : IBaz<T>
{
public void Method(T param)
{
Console.WriteLine(param.ToString());
}
}定義兩個有繼承關系的類型,IParent和SubClass。
interface IParent
{
void DoSomething();
}
public class SubClass : IParent
{
public void DoSomething()
{
Console.WriteLine("SubMethod");
}
}按照協(xié)變的邏輯,分別來使用IFoo和IBar。
//IFoo 不支持對參數(shù)T的協(xié)變
IFoo<SubClass> foo_sub = new FooClass<SubClass>();
IFoo<IParent> foo_parent = foo_sub;//編譯錯誤
//IBar 支持對參數(shù)T的協(xié)變
IBar<SubClass> bar_sub = new BarClass<SubClass>();
IBar<IParent> bar_parent = bar_sub;foo_parent = foo_sub 會提示編譯時錯誤“無法將類型“IFoo<SubClass>”隱式轉(zhuǎn)換為“IFoo<IParent>”。存在一個顯式轉(zhuǎn)換(是否缺少強制轉(zhuǎn)換?)”
按照逆變的邏輯,分別來使用IFoo和IBaz。
//IFoo 對參數(shù)T逆變不相容
IFoo<IParent> foo_parent = null;
IFoo<SubClass> foo_sub = foo_parent;//編譯錯誤
//IBaz 對參數(shù)T逆變相容
IBaz<IParent> baz_parent = null;
IBaz<SubClass> baz_sub = baz_parent;foo_sub = foo_parent 會提示編譯時錯誤“無法將類型“IFoo<IParent>”隱式轉(zhuǎn)換為“IFoo<ISub>”。存在一個顯式轉(zhuǎn)換(是否缺少強制轉(zhuǎn)換?)”
6、.NET4.0對IEnumerable接口的修改?
2.0中的定義:
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}4.0中的定義:
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}可以看到4.0中增加了對協(xié)變的支持。
可以在兩個版本試下, 下面的語句在2.0下會報錯。
List<SubClass> subarr = new List<SubClass>();
IEnumerable<IParent> parentarr = subarr;以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
C#實現(xiàn)將PDF轉(zhuǎn)為Excel的方法詳解
通常,PDF格式的文檔能支持的編輯功能不如office文檔多,針對PDF文檔里面有表格數(shù)據(jù)的,如果想要編輯表格里面的數(shù)據(jù),可以將該PDF文檔轉(zhuǎn)為Excel格式。本文將介紹如何利用C#實現(xiàn)PDF轉(zhuǎn)Excel,需要的可以參考一下2022-04-04

