.NET泛型中的协变与逆变
泛型的可变性:协变性和逆变性
实质上,可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用。
我们已经习惯了普通继承中的可变性:例如,若某方法声明返回类型为Stream,在实现时可以返回一个MemoryStream。泛型可变性的概念与此相同,但要略微复杂一些。可变性应用于泛型接口和泛型委托的类型参数中,这一点必须引起注意。
可变性有两种类型:协变性和逆变性。二者概念基本相同,只是在上下文中转换的方向不同。
我们先从协变性开始,它通常要好理解一些。
协变性:从API返回的值
协变性用于向调用者返回某项操作的值。例如一个简单的表示工厂模式的泛型接口,它只包含一个方法CreateInstanse,返回适当类型的实例。代码如下:
/// <summary>
/// 使用out关键字表示协变
/// </summary>
/// <typeparam name="T"></typeparam>
interface IFactory<out T>
{
T CreateInstance();
}
现在,T在接口中只出现了一次(除了在签名中),它仅作为返回值使用,即方法的输出。这意味着可以将特定类型的工厂视为更一般类型的工厂。如在现实世界里,你可以将比萨工厂视为食品工厂。
逆变性:传入API的值
逆变性则相反。它指的是调用者向API传入值,即API是在消费值,而不是产生值。我们来想象另一个简单的接口——它可以向控制台打印特定的文档类型。同样,它也只有一个方法Print:
/// <summary>
/// 使用in关键字表示逆变
/// </summary>
/// <typeparam name="T"></typeparam>
interface IPrettyPrinter<in T>
{
void Print(T document);
}
这次T只作为参数出现在了接口的输入位置。具体而言,如果我们实现了IPrettyPrinter<SourceCode>,就可以将其当作IPrettyPrinter<CSharpCode>来使用。
不变性:双向传递的值
如果协变性适用于仅从API输出值的情况,而逆变性用于仅向API输入值的情况,那么如果值双向传递会如何呢?简而言之,什么也不会发生。这种类型是不变体(invariant)。下面的接口表示可以对数据类型进行序列化和反序列化的类型:
/// <summary>
/// 泛型类型的不变性,既不用 in 关键字限制,也不用 out 关键字限制
/// </summary>
/// <typeparam name="T"></typeparam>
interface IStorage<T>
{
byte[] Serialize(T value); T Deserialize(byte[] data);
}
这时,如果存在一个具有特定类型T的IStorage<T>实例,我们不能将其视为该接口更具体或更一般类型的实现。如果以协变的方式使用(如将IStorage<Customer>视为IStorage<Person>),则可能在调用Serialize时传入一个无法处理的对象。
类似地,如果以逆变的方式使用,则可能在反序列化数据时得到一个预料之外的类型。如果有助于理解的话,可以将不变性看成ref参数:按引用传递变量,其类型必须与参数本身的类型完全一致,因为值被传入了方法内部,并且同样被高效地传出。
更多
详见MSDN:https://docs.microsoft.com/zh-cn/dotnet/standard/generics/covariance-and-contravariance
.NET泛型中的协变与逆变的更多相关文章
- Java泛型中的协变和逆变
Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛 ...
- C#4.0新增功能03 泛型中的协变和逆变
连载目录 [已更新最新开发文章,点击查看详细] 协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体 ...
- Scala中的协变,逆变,上界,下界等
Scala中的协变,逆变,上界,下界等 目录 [−] Java中的协变和逆变 Scala的协变 Scala的逆变 下界lower bounds 上界upper bounds 综合协变,逆变,上界,下界 ...
- C#4.0中的协变和逆变
原文地址 谈谈.Net中的协变和逆变 关于协变和逆变要从面向对象继承说起.继承关系是指子类和父类之间的关系:子类从父类继承所以子类的实例也就是父类的实例.比如说Animal是父类,Dog是从Anima ...
- .net中的协变和逆变
百度:委托中的协变和逆变. 百度:.net中的协变和逆变. 协变是从子类转为父类. 逆变是从父类到子类. 这样理解不一定严谨或者正确.需要具体看代码研究.
- [改善Java代码]警惕泛型是不能协变和逆变的
什么叫做协变(covariance)和逆变(contravariance)? 在变成语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数,泛型,返回值)替换或交换的特性,简单的说,协变是 ...
- Java语言中的协变和逆变(zz)
转载声明: 本文转载至:http://swiftlet.net/archives/1950 协变和逆变指的是宽类型和窄类型在某种情况下的替换或交换的特性.简单的说,协变就是用一个窄类型替代宽类型,而逆 ...
- Java中的协变与逆变
Java作为面向对象的典型语言,相比于C++而言,对类的继承和派生有着更简洁的设计(比如单根继承). 在继承派生的过程中,是符合Liskov替换原则(LSP)的.LSP总结起来,就一句话: 所有引用基 ...
- C#高级编程之泛型三(协变与逆变)
为何引入协变.逆变 我们知道一个子类对象可以赋值给一个基类对象 Animal animal = new Animal(); Animal cat = new Cat(); 那如果是用在泛型里面能行嘛? ...
随机推荐
- Maven 的基本配置与使用
什么是Maven Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. 发文时,绝大多数开发人员都把 Ant 当作 Java 编程项目的标准构 ...
- CDN基本工作过程
看了一些介绍CDN的文章,感觉这篇是讲的最清楚的. 使用CDN会极大地简化网站的系统维护工作量,网站维护人员只需将网站内容注入CDN的系统,通过CDN部署在各个物理位置的服务器进行全网分发,就可以实现 ...
- Installation failed with message...It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing.
错误弹窗如图: Installation failed with message Failed to finalize session: INSTALL_FAILED_TEST_ONLY:instal ...
- 大叔来说说Markdown的使用
强调和高亮背景 中国是伟大的民族! Highlight 中国是`伟大`的民族! ==Highlight== 链接 大叔博客园 [大叔博客园](http://www.cnblogs.com/lori & ...
- SSO - 开篇引例
进公司以来, 所做的产品中, 下面的子系统就没有少于10个的, 其中有的是.net做的, 有的是java做的, 还有安卓端, ios端. 那么这么多子系统, 我可能需要访问其中的多个(同一平台), 我 ...
- Linux内核升级导致无法启动,Kernel panic - not syncing Unable to mount root fs on unknown block(0,0)
问题原因:内核的某次升级,导致系统无法启动. 首先进入recovery模式:引导界面选择-->Ubuntu高级-->出现的选项中选择能够启动的recovery模式(几个内核版本分别试一下) ...
- mysql dbcp Caused By: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy ...
- SQL-结构化查询语言(2)
使用explain查询select查询语句的执行计划 mysql> explain select * from student where Sname='金克斯'\G ************* ...
- PHP面向对象——类
<?php/** * Created by PhpStorm. * User: 63448 * Date: 2018/5/6 * Time: 9:44 *///类:相似的数据和数据操作的封装 c ...
- Quart2D setNeedsDisplay
#import "myview.h" @interface myview () @property(nonatomic,assign) float imageY; @end @im ...