c# 协变和逆变的理解
1. 是什么
1.1 协变
协变指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。如 string 到 object 的转换。多见于类型参数用作方法的返回值。
1.2 逆变
逆变指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型。如 object 到 string 的转换。多见于类型参数用作方法的输入值。
泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。
2. 怎么理解
假如有一个 sub 子类和 parent 父类,我们可以很轻易地将 sub 转换成 parent,这是类型安全的,反之则不行。其实很好理解,子类必然具有相同或更多的属性和方法,所以转换成属性和方法可能更少的父类,只需要去除自身的部分属性和方法即可,这对编译器而言是可行的。反之,你怎么要求编译器为父类增加更多的成员呢。数组也继承了这一特性,对于一个string[]类型而言
理解了上述概念后,让我们来看看协变和逆变的概念,这里我们只谈谈关于接口可变性中的一些内容。以下我简单给出一个接口及其实现。
public interface IMyInTerface<T>
{
T Method1();
void Method2(T t);
}
public class MyClass<T> : IMyInTerface<T>
{
public T Method1()
{
return default(T);
}
public void Method2(T t)
{
Console.WriteLine(typeof(T).Name);
}
}
对应的调用方法代码如下
class Program
{
static void Main(string[] args)
{
// 协变演示
IMyInTerface<Sub> sub = new MyClass<Sub>();
Parent parent1 = sub.Method1();
// 逆变演示
IMyInTerface<Parent> parent2 = new MyClass<Parent>();
parent2.Method2(new Sub());
//// 协变演示
//IMyInTerface<Parent> parent = new MyClass<Parent>();
//Sub sub1 = parent.Method1();
//// 逆变演示
//IMyInTerface<Sub> sub2 = new MyClass<Sub>();
//sub2.Method2(new Parent());
Console.ReadKey();
}
}
以上调用代码都是正常的,但是当我们将上述代码的子类和父类的位置调换,换成上述注释中的代码,编译器则会报错。这便是违背了本文最重要的一个原则:类型转换中,对编译器而言只有子类到父类的转化才是安全的。
比如说,我们不能把 parent 的实例 赋值给 sub1 ,我们也不能把 new Parent() 代替 Sub 传入 Method2() 方法。
所以这一切,都是命运石之门的选择呀。
也正是因此,为了防止开发者写出错误的代码,.net 设计者便用了协变和逆变(对应 out 和 in 关键字)来强制要求正确行为。也就是说,即使你想这么做,一旦你用了微软提供的 IEnumerable 等接口,你也无法进行错误的类型转换了。所以归根到底,协变和逆变只是一种约束而已,这种规范限制了你的泛型接口中要么只能有将类型参数当作返回值的协变相容方法(加了 out 关键字),要么只能有将类型参数当作输入值的逆变相容方法(加了 in 关键字)。
本文针对的是对协变和逆变存在部分理解但是仍然有些迷糊的开发者群体,而笔者也忙于新技术的理解和投入使用,有段时间没能分享所学所得,这次也只是花了十几分钟撷取了重要概念记录答疑,希望能帮到一部分人,以上就是我的期望了。假如有不理解的,欢迎在评论区贴出即可。
- 删除 WordPress 导航菜单的多余 CSS 选择器(id或class)
- es6 对象的扩展
- 自定义WordPress 标签云小工具相关参数
- 检查.NET程序平台目标(Platform Target)工具CorFlags
- 哪些行业最适合抢驻小程序?
- WordPress 一键关闭/禁止页面评论功能
- 在一个.net sln中包含多个project,project引用同一个dll导致的错误
- Windows下安装MariaDB
- Quartz.net官方开发指南 第一课:使用Quartz.net
- Quartz.net官方开发指南 第二课:Jobs And Triggers
- 为你的WordPress 主题添加结构化数据/丰富文本摘要,高亮搜索结果(上)
- 复合事件处理(Complex Event Processing)介绍
- Quartz.net官方开发指南 第三课:更多关于Jobs和JobDetails
- 为你的WordPress 主题添加结构化数据/丰富文本摘要,高亮搜索结果(下)
- 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 数组属性和方法
- Yarr:一个UI很漂亮的RSS阅读器
- kotlin Standard中的内联函数示例详解
- 解决react-native软键盘弹出挡住输入框的问题
- flutter编写精美的登录页面
- Flutter实现App功能引导页
- Flutter底部不规则导航的实现过程
- Flutter实现用视频背景的登录页的示例代码
- Flutter实现可循环轮播图效果
- Android判断登录情况
- linux尝试登录失败后锁定用户账户的两种方法
- Linux内存泄漏检测shell脚本
- 详解Linux系统中网卡MAC地址克隆方法
- linux下日志定时轮询的流程详解
- Vim中查找替换及正则表达式的使用详解
- CentOS 7下部署php7.1和开启MySQL扩展的方法教程