前言

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

正文

什么是逆变和协变呢?

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

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

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

这里student 继承 person。

这里这个报错合情合理。

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

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

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

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

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

但是这样写就可以:

  1. static void Main(string[] args)
  2. {
  3. IEnumerable<Student> students = new List<Student>();
  4. IEnumerable<Person> peoples = students;
  5. }

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

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

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

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

协变怎么声明呢:

  1. public interface IEnumerable<out T> : IEnumerable
  2. {
  3. //
  4. // 摘要:
  5. // Returns an enumerator that iterates through the collection.
  6. //
  7. // 返回结果:
  8. // An enumerator that can be used to iterate through the collection.
  9. new IEnumerator<T> GetEnumerator();
  10. }

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

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

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

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

  1. public void test(T a)
  2. {
  3. }

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

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

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

  1. public interface ITest<in T>
  2. {
  3. public void Run(T obj);
  4. }
  5. public class Test<T> : ITest<T>
  6. {
  7. public void Run(T obj)
  8. {
  9. throw new NotImplementedException();
  10. }
  11. }

然后这样使用:

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

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

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

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

假如ITest 可以这样:

  1. public interface ITest<in T>
  2. {
  3. public T Run()
  4. {
  5. }
  6. }

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

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

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

下面是委托中的逆变:

  1. Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
  2. Action<Derived> d = b;
  3. 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. React版/Vue版都齐了,开源一套【特别】的后台管理系统...

    本项目主要基于Elux+Antd构建,包含React版本和Vue版本,旨在提供给大家一个简单基础.开箱即用的后台管理系统通用模版,主要包含运行环境.脚手架.代码风格.基本Layout.状态管理.路由管 ...

  2. ESX添加过时的硬件

    昨天遇到了个ESX上有旧的qlogic的FC卡无法识别的问题.无法识别也就无法连接存储,整个ESX就基本无法使用了.所以,需要检查硬件.一开始,在查了很多资料各种命令检查,走了很多弯路.这里我总结一下 ...

  3. AVL tree 高度上下界推导

    1. 最大高度对应 Node 数量 \(N_{h}\) 的递归公式 设有一棵 AVL tree 的高度为 \(h\), 对于该树, 构成该树的最少 node 数量为 \(N_{h}\) . 有: 最坏 ...

  4. 【Java UI】HarmonyOS添加日历事件

    ​参考资料 CalendarDataHelper Events Reminders api讲解 添加权限 在config.json添加权限代码如下 "reqPermissions" ...

  5. 腾讯云即时通信 IM 服务 实例项目

    腾讯云即时通信 IM 服务  https://github.com/tencentyun/TIMSDK

  6. 【学习笔记】循环神经网络(RNN)

    前言 多方寻找视频于博客.学习笔记,依然不能完全熟悉RNN,因此决定还是回到书本(<神经网络与深度学习>第六章),一点点把啃下来,因为这一章对于整个NLP学习十分重要,我想打好基础. 当然 ...

  7. ES重要配置解析

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

  8. 第五章:Admin管理后台 - 1:自定制Admin

    如果只是在admin中简单的展示及管理模型,那么在admin.py模块中使用admin.site.register将模型注册一下就好了: from django.contrib import admi ...

  9. [笔记] 二维FFT

    假设现在有2个矩阵a和b,分别是n行m列和x行y列,现在你要计算它们的二维卷积,也就是求出矩阵s满足: \(s_{i,j}=\sum_{i'\leq i,j'\leq j}a_{i',j'}b_{i- ...

  10. mysql工具的使用、增删改查

    mysql工具使用 目录 mysql工具使用 mysql的程序组成 mysql工具使用 服务器监听的两种socket地址 mysql数据库操作 DDL操作 数据库操作 表操作 用户操作 查看命令SHO ...