当前位置: 代码网 > it编程>编程语言>Asp.net > C#中协变逆变的实现

C#中协变逆变的实现

2026年01月13日 Asp.net 我要评论
1. 协变与逆变的概念协变(covariance)允许将子类(派生类)类型作为父类(基类)类型使用。例如:ienumerable<string>可以被视为ienumerable<ob

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# 协变逆变内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com