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
    // 定义协变接口:T 仅作为返回值(输出),加 out
    public interface IProducer<out T>
    {
    // 将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()
    {
    // 1. 正常创建 Dog 生产者
    IProducer<Dog> dogProducer = new DogProducer();

    // 2. 协变核心:IProducer<Dog> 可以赋值给 IProducer<Animal>
    IProducer<Animal> animalProducer = dogProducer;

    // 3. 调用 Produce,返回值可自动转为 Animal
    Animal animal = animalProducer.Produce(); // 实际返回的是 Dog 对象
    }
    }

    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
    // 定义逆变接口:T 仅作为方法参数(输入),加 in
    public interface IConsumer<in T>
    {
    // 将T作为输入
    void Consume(T value);
    }

    // 实现逆变接口:处理 Animal 的消费者
    public class AnimalConsumer : IConsumer<Animal>
    {
    public void Consume(Animal animal)
    {
    Console.WriteLine($"消费动物:{animal.GetType().Name}");
    }
    }

    // 调用示例
    class Program
    {
    static void Main()
    {
    // 1. 正常创建 Animal 消费者
    IConsumer<Animal> animalConsumer = new AnimalConsumer();

    // 2. 逆变核心:IConsumer<Animal> 可以赋值给 IConsumer<Dog>
    // 因为 T 是逆变(in),Animal → Dog 是向下兼容
    // 逻辑:能处理 Animal 的消费者,必然能处理 Dog(Dog 是 Animal 的子类)
    IConsumer<Dog> dogConsumer = animalConsumer;

    // 3. 调用 Consume,传入 Dog 对象,实际执行 AnimalConsumer 的逻辑
    dogConsumer.Consume(new Dog()); // 输出:消费动物:Dog
    }
    }
    头像头像
    ‘祈止’
    欢迎来到我的博客
    打赏作者
    本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ‘祈止’
    引用到评论