C#中协变逆变的实现

 更新时间:2026年01月13日 10:37:27   作者:FAREWELL00075  
本文详细介绍了C#中协变逆变,通过示例展示了协变和逆变在泛型接口和委托中的应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1. 协变与逆变的概念

  • 协变(Covariance)
    允许将子类(派生类)类型作为父类(基类)类型使用。例如:IEnumerable<string> 可以被视为 IEnumerable<object>,因为 string 是 object 的子类。按照前面我们在继承中学习的里氏替换原则,父类装子类,是不是就非常的合理,因为一定会确保类型安全嘛。

  • 逆变(Contravariance)
    允许将父类类型作为子类类型使用。例如:Action<object> 可以被视为 Action<string>,因为 object 是 string 的父类。你看,居然父类可以变成子类,你想想,你的父亲有一天居然可以化形为你的儿子来使用,是不是就很反常识,不过这种转化肯定是有限制滴,不然啥都可以转化,是不是就整个面向对象就乱成了一锅粥。

核心思想:在类型安全的前提下,允许更灵活的泛型类型转换。

2. 协变与逆变的作用及作用对象

作用

  •  高泛型接口和委托的灵活性,使其能更自然地处理继承关系。

  • 减少强制 类型转换,增强代码的可读性和安全性。

作用对象

  • 泛型接口(如 IEnumerable<T>)
  • 泛型委托(如 Func<T>、Action<T>)
  • 不适用:类、结构体或方法参数(仅支持接口和委托)。

请务必记住协变逆变的作用对象,仅仅只能在泛型接口和泛型委托中使用,在其他的地方是万万不行滴,不然就会天下大乱啦!!! 

3. 协变与逆变的关键字

out 关键字(协变)
标记泛型类型参数为协变,表示该参数仅用于输出位置(如返回值)。啥意思?就是说T这个泛型类型,在被out修饰了以后,他就只能被当成返回值的类型了,不能当做函数中传参时候的类型了,为什么呢?因为你总要做些区分嘛,要先列好规矩,互相才能正常运行嘛,不然,你玩儿你的,我打我打的,整个继承和多态就乱成了一锅粥。

interface ICovariant<out T> { T GetItem(); }

in 关键字(逆变)
标记泛型类型参数为逆变,表示该参数仅用于输入位置(如方法参数)。啥意思?就是说T这个泛型类型,在被in修饰了以后,他就只能被当成函数传参时候的类型了,不能当做返回值类型。

interface IContravariant<in T> { void Process(T item); }

4. 泛型接口与委托的示例

示例1:协变在泛型接口中的体现

// 协变接口定义
interface IAnimal<out T>
{
    T GetAnimal();
}

class Animal { }
class Dog : Animal { }

class AnimalShelter : IAnimal<Animal>
{
    public Animal GetAnimal() => new Animal();
}

class DogShelter : IAnimal<Dog>
{
    public Dog GetAnimal() => new Dog();
}

// 使用协变
IAnimal<Animal> shelter = new DogShelter(); // 合法:DogShelter → AnimalShelter
Animal animal = shelter.GetAnimal(); // 安全获取Animal类型

示例2:逆变在泛型接口中的体现

// 逆变接口定义
interface IFeeder<in T>
{
    void Feed(T animal);
}

class Animal { }
class Dog : Animal { }

class AnimalFeeder : IFeeder<Animal>
{
    public void Feed(Animal animal) => Console.WriteLine("Feeding animal");
}

class DogFeeder : IFeeder<Dog>
{
    public void Feed(Dog dog) => Console.WriteLine("Feeding dog");
}

// 使用逆变

IFeeder<Dog> feeder = new AnimalFeeder(); // 合法:AnimalFeeder → DogFeeder
feeder.Feed(new Dog()); // 安全传递Dog类型给需要Animal的方法
//打印Feeding animal

示例3:协变在泛型委托中的体现

// 协变委托
delegate T Factory<out T>();
Factory<Dog> dogFactory = () => new Dog();
Factory<Animal> animalFactory = dogFactory; // 合法:Dog → Animal
Animal animal = animalFactory();

示例4:逆变在泛型委托中的体现

// 逆变委托
delegate void Handler<in T>(T obj);
Handler<Animal> animalHandler = (Animal a) => Console.WriteLine("Handle animal");
Handler<Dog> dogHandler = animalHandler; // 合法:Animal → Dog
dogHandler(new Dog()); // 安全调用

总结

特性关键字方向适用场景
协变out子类→父类返回值、集合遍历(如 IEnumerable<T>)
逆变in父类→子类方法参数、回调处理(如 Action<T>)

协变:out修饰的泛型替代符,只能作为返回值,不能作为参数
逆变:in修饰的泛型替代符,只能作为参数,不能作为返回值
协变:父类委托容器可以装 子类泛型委托容器    
逆变:子类委托容器可以装 父类泛型委托容器

注意事项

  • 协变类型参数必须仅用于返回值位置。
  • 逆变类型参数必须仅用于方法参数位置。
  • 不支持类的协变/逆变,仅限接口和委托。

到此这篇关于C#中协变逆变的实现的文章就介绍到这了,更多相关C# 协变逆变内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C# params可变参数的使用注意详析

    C# params可变参数的使用注意详析

    这篇文章主要给大家介绍了关于C# params可变参数的使用注意的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-07-07
  • 关于C#中yield关键字的深入解析

    关于C#中yield关键字的深入解析

    这篇文章主要给大家介绍了关于C#中yield关键字的深入解析,文中通过示例代码介绍的非常详细,对大家的学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-11-11
  • C#异常处理的一些经验和技巧

    C#异常处理的一些经验和技巧

    本文是异常处理经验性的文章,其实跟C#关系也不大。比较适合刚刚熟悉异常语法,而缺乏实战的读者。当然,经验老练的读者也可指出不足、给予意见、补充说明,一起完善文章,分享更多知识与经验
    2014-03-03
  • 一文解析C#中的StringSplitOptions枚举

    一文解析C#中的StringSplitOptions枚举

    StringSplitOptions是C#中的一个枚举类型,用于控制string.Split()方法分割字符串时的行为,核心作用是处理分割后产生的“空字符串”或“空白字符串”,让开发者能精确控制分割结果的格式,本文给大家介绍C#中的StringSplitOptions枚举,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • c#操作json示例分享

    c#操作json示例分享

    这篇文章主要介绍了c#操作json示例,需要的朋友可以参考下
    2014-03-03
  • 微信开发--企业转账到用户

    微信开发--企业转账到用户

    本文主要介绍了微信开发--企业转账到用户的实现方法与步骤。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • Unity实现打砖块游戏

    Unity实现打砖块游戏

    这篇文章主要为大家详细介绍了Unity实现打砖块游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 如何使用C#从word文档中提取图片

    如何使用C#从word文档中提取图片

    图片和文字是word文档中两种最常见的对象,在微软word中,如果我们想要提取出一个文档内的图片,只需要右击图片选择另存为然后命名保存就可以了,今天这篇文章主要是实现如何使用C#从word文档中提取图片,需要的朋友参考下
    2016-02-02
  • c# 两种发送邮件的方法

    c# 两种发送邮件的方法

    这篇文章主要介绍了c# 两种发送邮件的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-04-04
  • C#中使用OpenCV的常用函数的常用示例

    C#中使用OpenCV的常用函数的常用示例

    这篇文章主要介绍了C#中使用OpenCV的常用函数的常用示例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02

最新评论