C#高级编程之泛型三(协变与逆变)
为何引入协变、逆变
我们知道一个子类对象可以赋值给一个基类对象
Animal animal = new Animal();
Animal cat = new Cat();
那如果是用在泛型里面能行嘛?
List<Animal> animalsList = new List<Animal>();//pass
List<Animal> CatsList = new List<Cat>();//error

一组猫难道不是一组动物吗?错,是因为这里List<Animal> 与List<Cat>分别是不同的类(用ILSpy.exe 即可以看到),这两个类没有继承关系。
当然此处可以采用List<Animal> CatsList = new List<Cat>().Select(c=>(Animal)c).ToList();来实现。
泛型中的类型对应的都是强类型,泛型在使用的时候,存在不和谐的地方,正是由于这个原因,出现了协变和逆变。
协变的使用
IEnumerable<Animal> catList = new List<Cat>();//协变
此处将一组Cat赋值给一组动物,符合我们正常的理解,是协调的变化。
具体看IEnumerable接口,其公开了一个T类型返回值的接口方法,通过一个Out关键字指定该类型支持协变。具体如下:
//
// 摘要:
// 公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。
//
// 类型参数:
// T:
// 要枚举的对象的类型。
[TypeDependencyAttribute("System.SZArrayHelper")]
public interface IEnumerable<out T> : IEnumerable
{
//
// 摘要:
// 返回一个循环访问集合的枚举器。
//
// 返回结果:
// 用于循环访问集合的枚举数。
IEnumerator<T> GetEnumerator();
}
这里IEnumerable是只读的,List是可以改的。如果List<string>可以变成List<object>的话,
即List<string>=List<object>;
那我往=List<object>里面add一个int怎么办?这样就类型不安全了,所以此处IEnumerable只能是支持协变的。
逆变的使用
协变逆变的定义
定义一个泛型接口
interface IFoo<T>
{
void Method1(T param);
T Method2();
}
如果我们允许协变,从IFoo<TSub>到IFoo<TParent>转换,那么IFoo.Method1(TSub)就会变成IFoo.Method1(TParent)。
我们都知道TParent是不能安全转换成TSub的,所以Method1这个方法就会变得不安全。
同样,如果我们允许反变IFoo<TParent>到IFoo<TSub>,则TParent IFoo.Method2()方法就会变成TSub IFoo.Method2(),
原本返回的TParent引用未必能够转换成TSub的引用,Method2的调用将是不安全的[返回值变成了具体的子类,父类的引用没法复制转换为子类引用了,违反LSP]。
故通过加入Out,In关键字来保证在泛型接口和泛型委托中,这种安全的类型转换。
总结
C#高级编程之泛型三(协变与逆变)的更多相关文章
- .NET 4.0中的泛型的协变和逆变
转自:http://www.cnblogs.com/jingzhongliumei/archive/2012/07/02/2573149.html 先做点准备工作,定义两个类:Animal类和其子类D ...
- 转载.NET 4.0中的泛型的协变和逆变
先做点准备工作,定义两个类:Animal类和其子类Dog类,一个泛型接口IMyInterface<T>, 他们的定义如下: public class Animal { } public ...
- Kotlin泛型与协变及逆变原理剖析
在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...
- Java用通配符 获得泛型的协变和逆变
Java对应泛型的协变和逆变
- Java泛型的协变与逆变
泛型擦除 Java的泛型本质上不是真正的泛型,而是利用了类型擦除(type erasure),比如下面的代码就会出现错误: 报的错误是:both methods have same erasure ...
- C#-弄懂泛型和协变、逆变
脑图概览 泛型声明和使用 协变和逆变 <C#权威指南>上在委托篇中这样定义: 协变:委托方法的返回值类型直接或者间接地继承自委托前面的返回值类型; 逆变:委托签名中的参数类型继承自委托方法 ...
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- C# 泛型的协变和逆变
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如 ...
- C# 泛型的协变和逆变 (转载)
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量. 协变和逆变是两个相互对立的概念: ...
随机推荐
- 【转】Centos7系统下忘记了root管理员账号密码的解决方式
哎,事情的起因就是脑子背了,曾经还手贱把root密码改了,导致普通账户改不回管理员账号了,然而,这次是百毒"救"了我,最终完美修改了root密码,好文章特地转载过来了,侵权删. 原 ...
- Percona Toolkit工具连接MySQL 8报错的解决方案
使用Percona Toolkit的工具连接MySQL 8.x数据库时,会遇到类似"failed: Plugin caching_sha2_password could not be loa ...
- 【DeepLearning】LeNet-5
手写字体识别模型LeNet5诞生于1994年,是最早的卷积神经网络之一.原文地址为Gradient-Based Learning Applied to Document Recognition,感谢网 ...
- 【源码】spring循环引用
spring在单例,非构造方法注入的情况下允许循环依赖 1.循环依赖 a引用b,b引用a.a创建的时候需要b,但是b没有创建,需要先去创建b,b创建的时候又没有a,这就出现的循环依赖的问题 2.为什么 ...
- Luogu P6280 [USACO20OPEN]Exercise G
题意 定义一个长度为 \(n\) 的置换的步数为将 \(P=(1,2,\cdots,n)\) 在该置换操作下变回原样的最小次数. 求所有 \(K\) 的和,使得存在一个长度为 \(n\) 的置换使得其 ...
- Windows10系统下Hadoop和Hive开发环境搭建填坑指南
前提 笔者目前需要搭建数据平台,发现了Windows系统下,Hadoop和Hive等组件的安装和运行存在大量的坑,而本着有坑必填的目标,笔者还是花了几个晚上的下班时候在多个互联网参考资料的帮助下完成了 ...
- 一次 KVM 虚拟机磁盘占满的排查过程
一次 KVM 虚拟机磁盘占满的排查过程 KVM 虚拟机系统为 CentOS,文件系统为 XFS. 现象如下: 使用 df -h 命令发现磁盘剩余空间为30k(总大小为30G),使用 df -i 发现 ...
- mysql增删改查语法
MySQL 关注公众号"轻松学编程"了解更多. MySQL是关系型数据库,对大小写不敏感. MySQL属于关系型数据库, 优点: 复杂查询 可以用SQL语句方便的在一个表以及多个表 ...
- 从小白到 6 个 offer,我究竟是怎么刷题的?
最近自习室里又兴起了一阵刷题潮,大家相约刷题~ 今天和大家系统分享下我去年转行时的一个刷题过程和方法,希望对你有所帮助. 首先介绍下我的编程基础,我学的是金融工程专业,硕士时学过 C++ 的课,这也是 ...
- Python3网络学习案例四:编写Web Proxy
代理服务器的定义和作用请走百度百科~ 1. Web Proxy的实现思路 这是基于上一篇"编写Web Server"写的,主要逻辑见下图: 我们要写的就是中间的Web Proxy部 ...