MSDN上的说法:
协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型----------(注意,这里说的可以使用更具体的类型是指可以给跟具体的类型传递相对不太具体的参数)。 泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。 在引用类型系统时,协变、逆变和不变性具有如下定义。 这些示例假定一个名为 Base 的基类和一个名为 Derived 的派生类。使你能够使用比原始指定的类型更泛型(派生程度更小)的类型。你可以向 IEnumerable<Derived> 类型的变量分配 IEnumerable<Base>(在 Visual Basic 中为 IEnumerable(Of Base))的实例。
感觉MSDN说的还是有些拗口。
逆变:Contravariance,类型参数用 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做参数的只能用做返回值,不能做输入参数。
我曾经写过一个随笔,实现中使用了协变的特性:由继承演变为角色扮演。