你了解C#的协变和逆变吗
- 从C# 4.0开始,泛型接口和泛型委托都支持协变和逆变,由于历史原因,数组也支持协变。
- 里氏替换原则:任何基类可以出现的地方,子类一定可以出现。
协变(out)
协变:即自然的变化,遵循里氏替换原则,表现在代码上则是任何基类都可以被其子类赋值,如Animal = Dog、Animal = Cat- 使用
out关键字声明(注意和方法中修饰参数的out含义不同) - 被标记的参数类型只能作为方法的返回值(包括只读属性)
- 在没有协变时:
abstract class Animal {}
class Dog : Animal {}
class Cat : Animal {} interface IPoppable<T>
{
T Pop();
}
class MyStack<T> : IPoppable<T>
{
private int _pos;
private readonly T[] _data = new T[100]; public void Push(T obj) => _data[_pos++] = obj;
public T Pop() => _data[--_pos];
}
以下代码是无法通过编译的
var dogs = new MyStack<Dog>();
IPoppable<Animal> animals1 = dogs; // 此处会发生编译错误
Stack<Animal> animals2 = dogs; // 此处会发生编译错误
此时,我们如果需要为动物园饲养员新增一个输入参数为
Stack<Animal>饲喂的方法,一个比较好的方法是新增一个约束泛型方法:class Zookeeper
{
public static void Feed<T>(IPoppable<T> animals) where T : Animal {}
}
// 或者
class Zookeeper
{
public static void Feed<T>(Stack<T> animals) where T : Animal {}
} // Main
Zookeeper.Feed(dogs);
- 现在,C#增加了协变
使IPoppable<T>接口支持协变// 仅仅增加了一个 out 声明
interface IPoppable<out T>
{
T Pop();
}
简化Feed方法
class Zookeeper
{
public static void Feed(IPoppable<Animal> animals) {}
} // Main
Zookeeper.Feed(dogs);
协变的天然特性——仅可作为方法返回值,接口(或委托)外部无法进行元素添加,确保了泛型类型安全性,所以不用担心Dog的集合中出现Cat
- 常用的支持协变的接口和委托有:
- IEnumerable
- IEnumerator
- IQueryable
- IGrouping<out TKey, out TElement>
- Func等共17个
- Converter<in TInput, out TOutput>
IEnumerable<Dog> dogs = Enumerable.Empty<Dog>();
IEnumerable<Animal> animals = dogs; var dogList = new List<Dog>();
IEnumerable<Animal> animals = dogList;
- 另外,由于历史原因,数组也支持协变,例如
var dogs = new Dog[10];
Animal[] animals = dogs;
但是无法保证类型安全性,以下代码可正常进行编译,但是运行时会报错
animals[0] = new Cat(); // 运行时会报错
逆变(in)
逆变:即协变的逆向变化,实质上还是遵循里氏替换的原则,将子类赋值到基类上- 使用
in关键字声明 - 被标记的参数类型只能作为方法输入参数(包括只写属性)
- 例如:
abstract class Animal {}
class Dog : Animal {}
class Cat : Animal {} interface IPushable<in T>
{
void Push(T obj);
}
class MyStack<T> : IPushable<T>
{
private int _pos;
private readonly T[] _data = new T[100]; public void Push(T obj) => _data[_pos++] = obj;
public T Pop() => _data[--_pos];
} // Main
var animals = new MyStack<Animal>();
animals.Push(new Cat());
IPushable<Dog> dogs = animals;
dogs.Push(new Dog());
逆变的天然特性——仅可作为方法输入参数,接口(或委托)无法进行元素获取,即只能将子类赋值到父类上,进而保证了类型安全性。
- 另外,常用支持逆变的接口和委托有:
- IComparer
- IComparable
- IEqualityComparer
- Action等共16个
- Predicate
- Comparison
- Converter<in TInput, out TOutput>
Action<Animal> animalAction = new Action<Animal>(a => { });
Action<Dog> DogAction = animalAction;
你了解C#的协变和逆变吗的更多相关文章
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- C#协变和逆变
我们知道在C#中,是可以将派生类的实例赋值给基类对象的.
- C# 泛型的协变和逆变
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如 ...
- 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事
阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...
- 再谈对协变和逆变的理解(Updated)
去年写过一篇博客谈了下我自己对协变和逆变的理解,现在回头看发现当时还是太过“肤浅”,根本没理解.不久前还写过一篇“黑”Java泛型的博客,猛一回头又是“肤浅”,今天学习Java泛型的时候又看到了协变和 ...
- 【转】c# 协变和逆变
本文转自:http://www.cnblogs.com/rr163/p/4047404.html C#的协变和逆变 由子类向父类方向转变是协变,用out关键字标识,由父类向子类方向转变是逆变,用in关 ...
- .NET 4.0中的泛型的协变和逆变
转自:http://www.cnblogs.com/jingzhongliumei/archive/2012/07/02/2573149.html 先做点准备工作,定义两个类:Animal类和其子类D ...
- 深入理解 C# 协变和逆变
msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型. “逆变”则是指能够使用派生程度更小的类型. 解释的很正确,大致就是这样,不过不够直白. 直白的理解: “协变” ...
- Java用通配符 获得泛型的协变和逆变
Java对应泛型的协变和逆变
- [改善Java代码]警惕泛型是不能协变和逆变的
什么叫做协变(covariance)和逆变(contravariance)? 在变成语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数,泛型,返回值)替换或交换的特性,简单的说,协变是 ...
随机推荐
- tensorflow1.0 构建神经网络做非线性归回
""" Please note, this code is only for python 3+. If you are using python 2+, please ...
- 测试Thread中的常用方法
package com.yhqtv.java; /* *测试Thread中的常用方法: * 1.start():启动当前线程:调用当前线程的run() * 2.run():通常需要重写Thread类的 ...
- Java 基础之详解 Java 反射机制
一.什么是 Java 的反射机制? 反射(Reflection)是Java的高级特性之一,是框架实现的基础,定义:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: ...
- Python执行Linux cmd命令,获取输出的一种方法,输出是bytes
import subprocess p = subprocess.Popen('df -lh', stdout=subprocess.PIPE, shell=True) print(p.stdout. ...
- 对于WebP格式入门解读
因为项目中需要用到大量动画效果,前期尝试过几种方案,比如GIF.帧动画.lottie.SVGA等格式的动画渲染方案,发现都存在各式各样的问题.比如: 1,GIF格式.5秒的动画,一张图大小可能就会达到 ...
- js 随机数生成器
title: js 随机数生成器 js 随机数生成器 js 随机数生成器 确定产生随机数的数目,最小值和最大值: 个数: 最小值: 最大值: 是否为唯一的随机数: 唯一 允许重复 点击生成产生随机数: ...
- 霍夫变换(Hough Transform)
霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法.最基本的霍夫变换是从黑白图像中检测直线(线段). 我们先看这样一个问题: 设已知一黑白图像上画了一条直线,要求出这 ...
- Spring boot自定义parent POM
文章目录 概述 不使用Parent POM来引入Spring boot 覆盖依赖项版本 概述 在之前的Spring Boot例子中,我们都会用到这样的parent POM. <parent> ...
- 【Netapp】在模拟器中使用disk removeowner报错
报错信息如下: Cluster2::storage disk*> removeowner NET-1.43 Error: command failed: Disk NET-1.43 is not ...
- Python-四则运算-蔡晓晴,杜婷萱
github链接:https://github.com/Amy-CC/Arithmetic-Operation 一.需求 1.使用-n 参数控制生成题目的个数 2.使用-r 参数控制题目中数值(自然数 ...