[C#]关于逆变与协变的基本概念和修饰符in与out的意义
协变与逆变的概念
假如两个类型X和Y具有特殊关系,X类型的每个值都能转换成Y类型。我们将I<X>向I<Y>的转换称为协变转换。反之我们将I<Y>向I<X>的转换称为逆变转换。
简单的举个例子:
List<string> str;
List<Object> obj;
我们知道在C#中所有引用类型都直接或间接地继承自Object类,所有string都可以转换成Object类,因此将str转换成obj是协变转换,反之则为逆变转换。
我们可以简单理解为:
由派生类向基类方向转变是协变
由基向派生类方向转变是逆变
关于in和out修饰符
下面用到的三个类:Animal类、Dog类、Cat类。
- 协变:一个
Cat[]也是Animal[] - 逆变:一个
Animal[]也是Cat[]
如果要避免类型错误,并且支持读写操作,那么只有第三种情况是安全的(不允许进行类型转换),Animal[]并不是总能当作Cat[],因为当一个客户读取数组并期望得到一个Cat,但Animal[]中包含的可能是个Dog。所以逆变规则是不安全的。
反之,一个Cat[]也不能被当作一个Animal[]。因为总是可以把一个Dog放到Animal[]中。在协变数组,这就不能保证是安全的,因为背后的存储可以实际是Cat[]。因此协变规则也不是安全的.
但是,只读数据类型是协变的(也就是说只需要数据只读,那么协变就是安全的),只写数据类型是逆变的(只需要数据只写,那么逆变就是安全的)。
这时候我们的in修饰符和out修饰符就派上用场了:
- 在C#4.0中使用out类型参数修饰符允许协变性,它会导致编译器验证该类型是否真的只作用于“输出”,即只用于方法的返回类型和只读属性的返回类型,永远不用于形参或者属性的赋值方法,如果验证通过则编译器会允许协变操作。
- 在C#4.0中使用in类型参数修饰符允许逆变性,它只是编译器核实类型从未在属性的取值方法(get访问器)中出现,也没有作为方法的返回类型使用,如果检查无误则会允许逆变操作。
关于协变转换的额一些限制
- 只有泛型接口和泛型委托才可以是协变的。泛型类和结构永远不是协变的。
- 协变的来源和目标必须是引用类型不能是值类型,也就是说不能一个是object,另一个是int。
接口或委托必须声明为支持协变,编译器必须验证协变所针对的类型参数确实只用在“输出”的位置。
参考:《C#6.0本质论》、wiki百科——协变与逆变、各种博客。
[C#]关于逆变与协变的基本概念和修饰符in与out的意义的更多相关文章
- Java中的逆变与协变
看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...
- scala 学习: 逆变和协变
scala 逆变和协变的概念网上有很多解释, 总结一句话就是 参数是逆变的或者不变的,返回值是协变的或者不变的. 但是为什么是这样的? 协变: 当s 是A的子类, 那么func(s) 是func(A) ...
- Scala 深入浅出实战经典 第81讲:Scala中List的构造是的类型约束逆变、协变、下界详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-97讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- C#4.0新特性(3):变性 Variance(逆变与协变)
一句话总结:协变让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值):逆变让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确. 通常,协 ...
- Java中的逆变与协变(转)
看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...
- Java 逆变与协变的名词说明
最近在研究Thinking in Java的时候,感觉逆变与协变有点绕,特意整理一下,方便后人.我参考于Java中的逆变与协变,但是该作者整理的稍微有点过于概念化,我在这里简单的说一下 我对于协变于逆 ...
- C# 逆变与协变
该文章中使用了较多的 委托delegate和Lambda表达式,如果你并不熟悉这些,请查看我的文章<委托与匿名委托>.<匿名委托与Lambda表达式>以便帮你建立完整的知识体系 ...
- Java 逆变与协变
最近一直忙于学习模电.数电,搞得头晕脑胀,难得今天晚上挤出一些时间来分析一下Java中的逆变.协变.Java早于C#引入逆变.协变,两者在与C#稍有不同,Java中的逆变.协变引入早于C#,故在形式没 ...
- .NET 4.0中的泛型逆变和协变
转载自:http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html:自己加了一些理解 随Visual Studi ...
- .NET:为什么需要逆变和协变
为啥需要协变和逆变? 我目前想到的理由是:逆变和协变的目的是支持多态. 一个小例子 不明白为啥输出的是false和true. using System; using System.Collection ...
随机推荐
- PHP redis有序集合实现分页
<?php //连接本地的 Redis 服务 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); //设置 redis 字符 ...
- pip cmd下载速度慢解决方案
cmd下载速度慢不是电脑问题,而是下载的网站有网速限制,如pip,虽然没被墙,但由于是外网,网速极差,经常是几KB一秒,所以我们可以采用镜像服务器,即在命令后加上 -i https://pypi.tu ...
- G1 垃圾收集器深入剖析(图文超详解)
G1(Garbage First)垃圾收集器是目前垃圾回收技术最前沿的成果之一. G1 同 CMS 垃圾回收器一样,关注最小时延的垃圾回收器,适合大尺寸堆内存的垃圾收集.但是,G1 最大的特点是引入分 ...
- Scrapy 如何传递 get请求的params
我们都知道 在requests中可以使用 requests.get(url,params)的方式传值 那么在scrapy中如何传值呢 直接看代码 from urllib.parse import ur ...
- [leetcode] 706. Design HashMap
题目 Design a HashMap without using any built-in hash table libraries. Implement the MyHashMap class: ...
- 嵌入式-C语言基础:怎么样使得一个指针指向固定的区域?
在学习单片机的时候,经常需要让一个指针指向固定的区域,这时候应该怎么操作? #include<stdio.h> int main() { int *p; int a =10; p=& ...
- C#使用附加到进程调试
微软官网的调试进程介绍 首先运行bin下的可执行文件,然后打开源代码,选择调试--->附加到进程.
- nginx的域名重写和转发案例
对url进行重写 location = /tongyong_OTA_1.0.3.bin { rewrite ^(.*)$ http://36.133.87.223/lecode-server/leco ...
- MobaXterm/Xshell快速命令宏配置
背景:使用MobaXterm或者Xshell连接远程服务器时,添加常用的命令宏一键执行,能提高效率节约时间. 使用方法: MobaXterm: 1)左边菜单栏->Macros->Recor ...
- 安装kali linux(干货)
安装kali 一. 准备工具 1. VMware Workstation Pro https://www.vmware.com/cn/products/workstation-pro/workstat ...