详解C#的协变和逆变
一、使用协变(Covariance)和逆变(Contravariance )能够实现数组之间、委托实例和方法之间、泛型委托实例之间、泛型接口的变量和泛型类型的对象之间、泛型接口的变量之间的隐式转换;使用协变将允许使用比原指定类型派生程度更大(即更具体的)的类型,使用逆变将允许使用比原指定类型派生程度更小(即更不具体的)的类型;
1.协变和逆变都只支持引用类型,不支持值类型;
2.如果泛型接口或泛型委托的类型参数被声明为协变或逆变,则该泛型接口或泛型委托被称为变体(Variant);
二、数组只支持协变,即支持派生程度更大的类型的数组隐式转换为派生程度更小的类型的数组:
object[] myArray = new string[5]; IComparable[] myOtherArray = new string[5];
1.此操作不是类型安全的,给上述数组添加原数组不兼容的对象时会抛出异常ArrayTypeMismatchException:
//myArray[0] = 10; //此处10会被装箱为object类型,而object类型的对象不能隐式转换为string类型
2.由于值类型不支持协变和逆变,因此下面的转换是错误的:
//object[] myArray = new int[5]; //IComparable[] myOtherArray = new int[5];
三、委托支持协变和逆变,为匹配委托类型和方法签名提供更大的灵活性,不仅可以将签名完全匹配的方法分配给委托实例,还可以通过协变将返回值类型与委托类型的返回值类型相比派生程度更大的方法分配给委托实例;通过逆变将参数类型与委托类型的参数类型相比派生程度更小的方法分配给委托实例:
public class MyBaseClass { } public class MyClass : MyBaseClass { } public class MyDerivedClass : MyClass { } public delegate MyClass MyDelegate(MyClass obj); public delegate T MyDelegate<T>(T obj); //对于给定的方法 public staitc MyDerivedClass MyFunc(MyBaseClass obj) { return new MyDerivedClass(); } //使用时: MyDelegate myDelegate = MyFunc; //同时使用了协变和逆变 MyDelegate<MyClass> myDelegate = MyFunc;
四、在定义泛型委托时,可以通过将类型参数声明为逆变量或协变量来定义变体委托(Variant Delegate),从而使具有协变和逆变转换关系的不用类型的泛型委托实例之间进行隐式转换,使用关键字out修饰返回值的类型参数以支持协变,使用关键字in修饰参数的类型参数以支持逆变:
public delegate U MyDelegate<in T, out U>(T obj); //使用时: MyDelegate<MyClass,MyClass> myDelegate = MyFunc; MyDelegate<MyBaseClass, MyDerivedClass> myOtherDelegate = MyFunc; //隐式转换: myDelegate = myOtherDelegate;
1.变体委托不支持合并,即Delegate.Combine方法需要委托的类型完全相同才能合并,不支持变体委托的转换;在运行时合并不同类型的变体委托会抛出异常System.ArgumentException;
五、在定义泛型接口时,可以通过将类型参数声明为逆变量或协变量来定义变体接口(Variant Interface),协变允许方法的返回值类型比接口定义中返回值类型参数的派生程度更大,逆变允许方法的参数类型比接口定义中参数类型参数的派生程度更小;使用关键字out修饰返回值的类型参数以支持协变,使用关键字in修饰参数的类型参数以支持逆变:
public interface IMyInterface<in T, out U> { U MyFunc(); void MyFoo(T obj); } public class MyClass<T, U> : IMyInterface<T, U> { public void MyFoo(T obj) { //do… } } //使用时: IMyInterface<string, object> myObj = new MyClass<object, string>(); IMyInterface<object, string> myObj1 = new MyClass<object, string>(); myObj = myObj1;
1.实现变体接口的类型仍然是不可变量;
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
- 1668: [Usaco2006 Oct]Cow Pie Treasures 馅饼里的财富
- 2463: [中山市选2009]谁能赢呢?
- 2748: [HAOI2012]音量调节
- 2697: 特技飞行
- 我这么玩Web Api(二)
- 1296: [SCOI2009]粉刷匠
- 1293: [SCOI2009]生日礼物
- 记一次线程池调优经历
- JavaScript对象
- 1088: [SCOI2005]扫雷Mine
- 1029: [JSOI2007]建筑抢修
- 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)
- 关于类的对象创建与初始化
- 1191: [HNOI2006]超级英雄Hero
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- TSF微服务无法查看JVM监控问题
- Mitmproxy 的安装与使用案例
- leetcode之Bigram分词
- TensorFlow2 开发指南 | 01 手写数字识别快速入门
- Spring 数据初始 H2 后进行数据查询提示 Schema not found 错误
- Discourse CentOS 8 全新安装手册
- H2 数据库插入时间的方法
- Spring 配置的 H2 控制台 frameOptions 导致无法访问
- Spring Boot 和 Hibernate 的 H2 数据库配置来进行启动测试
- Java时间处理-LocalDateTime简介
- iOS音视频接入 - TRTC iOS端真机日志导出
- jQuery根据填写的input的数值导出excel表格
- 小程序根据返回值英文渲染出对应的中文
- 小程序返回的时间戳转化成时间
- 小程序使用 组件库 vant-weapp详细教程