一、C#中的协变
众所周知,子类对象可以赋值给父类,例如 string str = string.Empty; object obj = str; 不会报错。如果换一种方式,代码如下:
public class Father
{
private string Name { get; set; }
}
public class Son : Father
{
private string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Father> fathers = new List<Son>();
}
}
很显然List fathers = new List();这句会报错,因为虽然Son和Father之间存在继承关系,但是两个List之间却不存在继承关系。 因此在C#4.0中引入协变的概念,使用协变,可以看到List实现了IEnumerable接口,而IEnumerable接口的定义:
namespace System.Collections.Generic
{
//
// 摘要:
// 公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。
//
// 类型参数:
// T:
// 要枚举的对象的类型。
public interface IEnumerable<out T> : IEnumerable
{
//
// 摘要:
// 返回一个循环访问集合的枚举器。
//
// 返回结果:
// 用于循环访问集合的枚举数。
IEnumerator<T> GetEnumerator();
}
}
在泛型T前面有一个out修饰符来修饰这个泛型,这时改变之前的写法:
IEnumerable<Father> fathers = new List<Son>();
发现不再报错,这就是协变。
二、C#中的逆变
逆变的关键字修饰符是in,这里我们自定义一个逆变:
public interface IPerson<in T>
{
}
public class Person<T> : IPerson<T>
{
}
public class Father
{
private string Name { get; set; }
}
public class Son : Father
{
private string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
IPerson<Son> person = new Person<Father>();//逆变
}
}
这样,父类Father泛型类型的对象可以赋值给子类泛型类型,这就是C#中的逆变。