技术C#CSharp笔记——泛型变体
‘祈止’前提:泛型变体是C#针对泛型接口/委托的特向,用于让泛型类型参数的继承关系能传递到泛型类型本身。已实现里氏替换原则。
例如:允许将IEnumerable赋值给IEnumerable,这就是协变。反之就是逆变。
一、铺垫
1.泛型的不变性
比如,Dog:Animal(Dog是Animal的子类),但是**List不是List**的子类。这就是泛型的“不变性”(Invariance)。
2.变体的规则
- 变体只适用于泛型接口/委托,类、结构体、方法的泛型不支持。就比如上述的List是类所以不变。
- 仅适用于引用类型,因为变体依赖于OOP的向上转型机制,所以值类型不知此变体。比如A不能复制给A
二、变体的核心类型
C#提供两个关键字用来声明变体:out(协变)和in(逆变)。
1.协变(Covariance):out
1.要点:泛型类型参数只能作为返回值/输出,此时泛型类型能顺着继承链向上兼容(子类—>父类)。
**2.理解:**因为多态的特性,子类对象可以赋值给父类变量(向上转型)。返回一个父类对象时,可以返回子类。
3.示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public interface IProducer<out T> { T Produce(); }
public class Animal { } public class Dog : Animal { }
public class DogProducer : IProducer<Dog> { public Dog Produce() => new Dog(); }
class Program { static void Main() { IProducer<Dog> dogProducer = new DogProducer(); IProducer<Animal> animalProducer = dogProducer; Animal animal = animalProducer.Produce(); } }
|
2.逆变(Contravariance):in
1.要点:泛型类型参数只能作为参数/输入,此时泛型类型能逆继承链向下兼容(父类—>子类)。
**2.理解:**因为向上转型,接受父类对象时可以接受子类的传入。
3.示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public interface IConsumer<in T> { void Consume(T value); }
public class AnimalConsumer : IConsumer<Animal> { public void Consume(Animal animal) { Console.WriteLine($"消费动物:{animal.GetType().Name}"); } }
class Program { static void Main() { IConsumer<Animal> animalConsumer = new AnimalConsumer(); IConsumer<Dog> dogConsumer = animalConsumer; dogConsumer.Consume(new Dog()); } }
|