协变与逆变的概念


假如两个类型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访问器)中出现,也没有作为方法的返回类型使用,如果检查无误则会允许逆变操作。

关于协变转换的额一些限制

  1. 只有泛型接口和泛型委托才可以是协变的。泛型类和结构永远不是协变的。
  2. 协变的来源和目标必须是引用类型不能是值类型,也就是说不能一个是object,另一个是int。
  3. 接口或委托必须声明为支持协变,编译器必须验证协变所针对的类型参数确实只用在“输出”的位置。

    参考:《C#6.0本质论》、wiki百科——协变与逆变、各种博客。

[C#]关于逆变与协变的基本概念和修饰符in与out的意义的更多相关文章

  1. Java中的逆变与协变

    看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...

  2. scala 学习: 逆变和协变

    scala 逆变和协变的概念网上有很多解释, 总结一句话就是 参数是逆变的或者不变的,返回值是协变的或者不变的. 但是为什么是这样的? 协变: 当s 是A的子类, 那么func(s) 是func(A) ...

  3. Scala 深入浅出实战经典 第81讲:Scala中List的构造是的类型约束逆变、协变、下界详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-97讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  4. C#4.0新特性(3):变性 Variance(逆变与协变)

    一句话总结:协变让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值):逆变让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确. 通常,协 ...

  5. Java中的逆变与协变(转)

    看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...

  6. Java 逆变与协变的名词说明

    最近在研究Thinking in Java的时候,感觉逆变与协变有点绕,特意整理一下,方便后人.我参考于Java中的逆变与协变,但是该作者整理的稍微有点过于概念化,我在这里简单的说一下 我对于协变于逆 ...

  7. C# 逆变与协变

    该文章中使用了较多的 委托delegate和Lambda表达式,如果你并不熟悉这些,请查看我的文章<委托与匿名委托>.<匿名委托与Lambda表达式>以便帮你建立完整的知识体系 ...

  8. Java 逆变与协变

    最近一直忙于学习模电.数电,搞得头晕脑胀,难得今天晚上挤出一些时间来分析一下Java中的逆变.协变.Java早于C#引入逆变.协变,两者在与C#稍有不同,Java中的逆变.协变引入早于C#,故在形式没 ...

  9. .NET 4.0中的泛型逆变和协变

    转载自:http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html:自己加了一些理解 随Visual Studi ...

  10. .NET:为什么需要逆变和协变

    为啥需要协变和逆变? 我目前想到的理由是:逆变和协变的目的是支持多态. 一个小例子 不明白为啥输出的是false和true. using System; using System.Collection ...

随机推荐

  1. lombok下载和安装

    lombok是什么 第三方的组件:使用注解来简化类的编写,注解替换set/get/构造 注解: @setter @getter @NoArgsConstructor @AllArgsConstruct ...

  2. JavaScript基础&实战(1)js的基本语法、标识符、数据类型

    文章目录 1.JavaScript简介 2.输出语句 2.1 代码块 2.2 测试结果 3.JS编写位置 3.1代码 3.2 测试结果 4.基本语法 4.1 代码 5.标识符 5.1 代码 6.数据类 ...

  3. 独立按键控制led灯

    #include "regx51.h"typedef unsigned int u16; void delay_us(u16 time){ while(time--){} }voi ...

  4. 常用CSS样式属性

    01.常用样式 1.1.background背景 设置元素背景的样式 background,更好的衬托内容. 属性 描述 值 background 背景属性简写.支持多组背景设置,逗号,隔开 back ...

  5. 2022-11-11 Acwing每日一题

    本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的.同时也希望 ...

  6. 基于python的数学建模---场线与数值解(微分方程)

    import numpy as np from scipy import integrate import matplotlib.pyplot as plt import sympy def plot ...

  7. C温故补缺(十六):未定义行为

    未定义行为 在计算机程序设计中,未定义行为是指执行某种计算机代码 所产生的结果,这种代码在当前程序状态下的行为在其所使用的语言标准中没有规定. 以C语言为例,未定义行为指C语言标准未作规定的行为,同时 ...

  8. C温故补缺(九):字节对齐与排序

    字节对齐与排序 字节对齐的原因与字节排序 取自:VisualEther 原文档下载:Gitee _packed _packet用于结构体中变量在内存中的对齐.如 typedef struct test ...

  9. 关于解决 inittramfs unpacking failed:Decoding failed 报错

    解决办法 vi /etc/initramfs-tools/initramfs.conf 更改COMPRESS=lz4以COMPRESS=gzip 保存更改 sudo update-initramfs ...

  10. flutter系列之:在flutter中使用流式布局

    目录 简介 Flow和FlowDelegate Flow的应用 总结 简介 我们在开发web应用的时候,有时候为了适应浏览器大小的调整,需要动态对页面的组件进行位置的调整.这时候就会用到flow la ...