.NET中的逆变协变
MSDN上的说法:
IEnumerable(Of Base))的实例。
感觉MSDN说的还是有些拗口。
in 关键字。
简单来说就是假设有一个泛型逆变接口IDo<in T>,有两个可以替换T的类型A和B时,B如果继承自A时,可以将IDo<A>的实例赋值给IDo<B>,例如:
private void button1_Click(object sender, EventArgs e)
{
IDo<A> a = new DoT<A>();
IDo<B> b = new DoT<B>();
Run run = new Run();
run.Set<D>(a);
}
interface IDo<in T>
{
void Do(T t);
}
class A { }
class B : A { }
class D : B { }
class C : A { }
class E : C { }
class DoT<T> : IDo<T>
{
public void Do(T t)
{
MessageBox.Show(t.GetType().ToString());
}
}
class Run
{
public void Set<T>(IDo<T> it) where T : new()
{
it.Do(new T());//返回D的类型
MessageBox.Show(it.GetType().ToString());//返回A的接口类型
}
}
协变:Covariance,类型使用out关键字。
这个相对于逆变来说更容易理解,当有接口IDo<out T>时,有两个可以替换T的类型A和B时,B如果继承自A时,可以将IDo<B>的实例赋值给IDo<A>,说这更容易理解是因为里氏替换大家会熟悉些,这个看起来有些像,不过不要混淆,这是两回事,例子:
interface IDo<out T>
{
T get();
}
private void button1_Click(object sender, EventArgs e)
{
IDo<A> a = new DoT<A>();
IDo<B> b = new DoT<B>();
Run run = new Run();
run.Set<A>(b);
}
另外需要注意的,使用了in的T只能用做输入参数,不能用做返回值。使用了out做参数的只能用做返回值,不能做输入参数。
我曾经写过一个随笔,实现中使用了协变的特性:由继承演变为角色扮演。