前言

简单整理一下逆变和协变。

正文

什么是逆变和协变呢?

首先逆变和协变都是术语。

协变表示能够使用比原始指定的派生类型的派生程度更大的类型。

逆变表示能够使用比原始指定的派生类型的派生程度更小的类型。

这里student 继承 person。

这里这个报错合情合理。

这里可能有些刚入门的人认为,person 不是 student 的父类啊,为什么不可以呢?

一个列表student 同样也是一个列表的 person啊。

这可能是初学者的一个疑问。

但是实际情况是list 是一个类型, list 是一个类型。

所以他们无法隐式转换是正常的。

但是这样写就可以:

static void Main(string[] args)
{
IEnumerable<Student> students = new List<Student>();
IEnumerable<Person> peoples = students;
}

这样写没有报错,理论上IEnumerable是一种类型,IEnumerable是一种类型,不应该能隐私转换啊。

为什么呢?因为支持协变。

协变表示能够使用比原始指定的派生类型的派生程度更大的类型。

他们的结构如上。因为student是person的派生类,IEnumerable的派生程度比IEnumerable大。

协变怎么声明呢:

public interface IEnumerable<out T> : IEnumerable
{
//
// 摘要:
// Returns an enumerator that iterates through the collection.
//
// 返回结果:
// An enumerator that can be used to iterate through the collection.
new IEnumerator<T> GetEnumerator();
}

这里协变有个特点,那就是协变参数T,只能用于返回类型。

原因是在运行时候还是new List(),返回自然是Student,那么student 可以赋值给person,这没问题。

那么协变参数T,不能用于参数呢? 是这样的。

比如 IEnumerable里面有一个方法是:

public void test(T a)
{ }

在IEnumerable 中原本要传入一个Student,现在使用了IEnumerable,那么就可以传入person。

person 要转换成student,显然是不符合的。

那么协变是这样的,那么逆变呢?

public interface ITest<in T>
{
public void Run(T obj);
} public class Test<T> : ITest<T>
{
public void Run(T obj)
{
throw new NotImplementedException();
}
}

然后这样使用:

static void Main(string[] args)
{
ITest<Person> students = new Test<Person>();
ITest<Student> peoples = students;
peoples.Run(new Student());
}

这里的逆变只能作用于参数。

先说一下为什么能够作用于参数,就是在运行的时候本质还是new Test(),要传递的是一个person,如果传递一个student,那么也是可以的。

然后为什么不能作用于返回值呢?

假如ITest 可以这样:

public interface ITest<in T>
{
public T Run()
{
}
}

在运行时候是Test(),那么调用run返回的是person,但是赋值给了Student类型,和上面同样的问题哈。

所以协变不能作用于参数,逆变不能作用于返回值。

那么也就是说要摸只能协变,要摸只能逆变。

下面是委托中的逆变:

Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());

原理就是Derived继承自Base,原本需要传入base,现在传入Derived,当然也是可以的。

之所以这么设计是一个哲学问题,那就是子类可以赋值给父类,父类能办到的子类也能办到,他们分别对应的是协变和逆变。

下一节委托。

重学c#系列——逆变和协变[二十四]的更多相关文章

  1. 重学c#系列——字典(十一)

    前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...

  2. Java中的逆变与协变

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

  3. scala 学习: 逆变和协变

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

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

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

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

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

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

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

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

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

  8. C# 逆变与协变

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

  9. Java 逆变与协变

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

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

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

随机推荐

  1. Java SE 枚举,注解,增强for循环

    Java SE 进阶 1.Enum 枚举对象名通常使用全部大写,常量的命名规范 构造器私有化 本类内部创建一组对象 对外暴露对象(通过为对象添加 public final static 修饰符) 可以 ...

  2. Grafana Loki 学习之踩坑记

    转发自:https://mp.weixin.qq.com/s/zfXNEkdDC9Vqd9lh1ptC1g Grafana 出品的 loki 日志框架完美地与 kubernetes 的 label 理 ...

  3. service服务使用CoreDNS提供的域名地址访问

    普通的 Service:会生成 servicename.namespace.svc.cluster.local 的域名,会解析到 Service 对应的 ClusterIP 上,在 Pod 之间的调用 ...

  4. ES重要配置解析

    path.data和path.logs 如果您使用.zip或.tar.gz存档,则data和logs 目录是子文件夹$ES_HOME.如果这些重要文件夹保留在其默认位置,则在将Elasticsearc ...

  5. kubeoperator升级步骤

    当前kubeoperator版本是3.6.0 官方文档:https://kubeoperator.io/docs/installation/install/ # 离线升级需要提前下载离线安装包,并解压 ...

  6. Elastic:Sense chrome - 在 chrome 浏览器中操作 Elasticsearch 的插件

    文章转载自:https://elasticstack.blog.csdn.net/article/details/114533904 Sense 是我们现在所说的 Kibana 控制台 UI 的第一个 ...

  7. PR中导入的照片在画面上只显示一部分的解决办法

    这是因为图片的分辨率太大导致的. 1.图片的分辨率大于PR工程的设置的时候,就会只显示局部. 2.可以用PS把图片先缩小为工程大小即可. 3.也可以把图片拖到时间线上,右键点击,缩放为帧大小即可. 这 ...

  8. WiresShark

    WireShark 分析数据包技巧 确定WireShark的位置[是否在公网上] 选择捕获接口,一般都是internet网络接口 使用捕获过滤器 使用显示过滤器[捕获后的数据包还是很复杂,用显示过滤器 ...

  9. 分支结构之二:switch-case

    1.格式 switch(表达式){case 常量1: 执行语句1; //break; case 常量2: 执行语句2; //break; ... default: 执行语句n; //break; } ...

  10. 纯Vue实现网页日常任务清单小功能(数据存储在浏览器)

    任务清单可以极大提高我们的工作效率.哪些事情办了.哪些事情代办.哪些是紧急需要办的事情等等. 在组件化编码实战3的基础上进一步改进.将原先的数据保存的浏览器中.就可以做到关闭网页也不会丢失数据的情况 ...