.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(); 那如果是用在泛型里面能行嘛? ...
随机推荐
- JavaScript -- Window-Blur
-----030-Window-Blur.html----- <!DOCTYPE html> <html> <head> <meta http-equiv=& ...
- 单例模式——java设计模式
单例模式 目录: 一.何为单例 二.使用Java EE实现单例模式 三.使用场景 一.何为单例 确保一个类只有一个实例,并且提供了实例的一个全局访问点 1.1 单例模式类图 ...
- Windows x86 下的 静态代码混淆
0x00 前言 静态反汇编之王,毫无疑问就是Ida pro,大大降低了反汇编工作的门槛,尤其是出色的“F5插件”Hex-Rays可以将汇编代码还原成类似于C语言的伪代码,大大提高了可读性.但个人觉得 ...
- OSI与TCP/IP模型
1 OSI参考模型 谈到网络不能不谈OSI参考模型,虽然OSI参考模型的实际应用意义不是很大,但其的确对于理解网络协议内部的运作很有帮助,也为我们学习网络协议提供了一个很好的参考.在现实网络世界里,T ...
- Servlet 监听器Listener详解
转自:http://blog.csdn.net/u012228718/article/details/41730799 一.简介 (一)概述 1.Listener 用于监听 Javaweb程序中的事件 ...
- SOA和微服务架构的区别?
转自知乎:https://www.zhihu.com/question/37808426/answer/93335393 SOA和微服务架构的区别? 微服务架构强调的第一个重点就是业务系统需要彻底的组 ...
- 开源方案搭建可离线的精美矢量切片地图服务-7.Arcgis Pro企业级应用
1.前言 上篇讲.pbf字体库的时候说到我们使用的字体通过Arcgis Pro 生成,Arcgis Pro样式基于Mapbox做的矢量切片地图渲染.这篇主要讲一下Arcgis Pro矢量切片生成的的具 ...
- 并发编程——ConcurrentHashMap#transfer() 扩容逐行分析
前言 ConcurrentHashMap 是并发中的重中之重,也是最常用的数据结果,之前的文章中,我们介绍了 putVal 方法.并发编程之 ConcurrentHashMap(JDK 1.8) pu ...
- 为MVC应用程序创建导航条
今晚写点什么呢?对于以前的练习,看来看去,好象还差一个菜单导航条.在练习的站点中,有创建了三个控制器,我们就用它们来创建一个导航条吧.想实现这导航条,刚开始还是有点难,还是想起以前ASP.NET的Me ...
- jQuery实现DropDownList(MVC)
今天学点什么呢?想来想去觉得DropDownList还是要加强功力,因为以前写ASP.NET网站时,此功能用的非常之多.在练习之前,先温习一下以前练习的<Html.DropDownListFor ...