逆变和协变是C# 4.0出来的,个人认为这是对泛型的扩充,让C#的语法更加灵活,方便重用。只有泛型接口和泛型委托可以是协变的或逆变的。
泛型中有out in关键字,用于定义此泛型是遵守逆变还是协变。out限制了泛型只能用于返回值,in限制了泛型只能用于参数。
之前的泛型是可以指定对某一类操作,但是无法把类的子类,父类赋值转换。有了逆变和协变,实现了泛型类的父类子类的转换。
协变 (covariant)关键字是out,out限制了泛型只能用于返回值
示例代码如:
public interface IEnumerable<out T>
public static void Main()
{
Dog aDog = new Dog();
Animal aAnimal = aDog;
IEnumerable<Dog> someDogs = new List<Dog>();
IEnumerable<Animal> someAnimals = someDogs;
}
现在我们有Animal父类,Dog, Cat子类
public class Animal
{
public string type;
public Animal()
{
type = "Animal";
}
}
public class Dog : Animal
{
public Dog()
{
type = "dog";
}
}
public class Cat : Animal
{
public Cat()
{
type = "Cat";
}
}
如果我们使用List<T>泛型,那我们在List<Animal>和List<Cat>是不能互相转换的。下面的代码是编译不通过的
List<Animal> animalList = new List<Dog>();
如果要实现这个转换必须要使用如下的方法
animalList = dogList.Select(o => (Animal)o).ToList();
但现在我们有这样的方法
public void PrintAnimals(IEnumerable<Animal> animals)
{
foreach (var item in animals)
{
Console.WriteLine(item.type);
}
}
就可以能传递List<Cat>了, 也能传递List<Animal>,因为IEnumerable<out T>有out标记,表示可以协变,所以编译通过。但是在内部,IL还是帮我做了一个个元素依次转换
的工作。这样可以减少了我们的代码量,让程序更灵活。
逆变(contravariant)关键字是in,in限制了泛型只能用于参数
下面以Action<in T>为例
public delegate void Action<in T>
public static void Main()
{
Action<Animal> actionAnimal = new Action<Animal>(a => {/*让动物叫*/ });
Action<Dog> actionDog = actionAnimal;
actionDog(aDog);
}
编译通过,因为 Action支持逆变,类型向下转换
作用:我个人肤浅的理解,之前的泛型是可以指定对某一类操作,但是无法把类的子类,父类赋值转换。有了逆变和协变,实现了泛型类的父类子类的转换。目的是为了增加语言的灵活性和能力。使泛型和委托的功能更加强大。