C#协变与逆变
http://zh.wikipedia.org/wiki/%E5%8D%8F%E5%8F%98%E4%B8%8E%E9%80%86%E5%8F%98
协变与逆变是程序设计语言中的类型系统的一对概念。类型系统支持子类型。例如,如果Cat是Animal的子类型,那么Cat类型的表达式可用于任何出现Animal类型表达式的地方。 变型(variance)是指,如何根据其组成类型来确定更复杂的类型(如Cat列表对Animal列表,返回Cat的函数对返回Animal的函数,...,等等。)之间的子类型关系。依赖于类型构造器的变型性质,复杂类型的子类型性质可分为保持、逆转、与忽略。例如,在C Sharp中:
- IEnumerable<Cat>是IEnumerable<Animal>的子类型,因为IEnumerable<T>类型构造器是协变(covariant)。也即,界面的最初的类型参数的子类型关系保持住了。
- Action<Cat>是Action<Animal>的超类型,因为Action<T>类型构造器是逆变(contravariant),其中Action<T>表示一个头等函数,需要一个类型参数为T或sub-T。注意对于T的子类型关系,在Action复杂类型封装下是逆转的。
- IList<Cat>或IList<Animal>彼此之间没有子类型关系。因为IList<T>类型构造器是不变(invariant).
程序语言的设计者需要考虑针对数组、继承、泛型数据类型等的类型规则的“变型”。通过使得类型构造器是协变、逆变而不是“不变”,使得更多的程序可作为良好类型被接受。另一方面,程序员经常觉得逆变是不直观的;如果为避免运行时刻错误而精确跟踪变型将导致复杂的类型规则。为了保持类型系统简单,允许有用的编程,程序设计语言可把类型构造器处理为“不变”,即使它其实作为“变型”也是类型安全的;或者把类型构造器处理为协变,即使这会导致违背类型安全。
using System; namespace TestCovarianceContravariance
{
public class Shape
{
public double Width { get; set; }
public double Height { get; set; } public override string ToString()
{
return String.Format("Width: {0}, Height: {1}", Width, Height);
}
} public class Rectangle : Shape
{
} //如果泛型类型用out关键字标注,泛型接口就是协变的。这也意味着返回类型只能是T。
//接口IIndex与类型T是协变的,并从一个只读索引器中返回这个类型
public interface IIndex<out T>
{
T this[int index] { get; }
int Count { get; }
} public class RectangleCollection : IIndex<Rectangle>
{
private Rectangle[] data = new Rectangle[3]
{
new Rectangle {Height = 2, Width = 5},
new Rectangle {Height = 3, Width = 7},
new Rectangle {Height = 4.5, Width = 2.9}
}; public static RectangleCollection GetRectangles()
{
return new RectangleCollection();
} public Rectangle this[int index]
{
get
{
if (index < 0 || index > data.Length)
throw new ArgumentOutOfRangeException("index");
return data[index];
}
} public int Count
{
get { return data.Length; }
}
} internal class Program
{
private static void Main(string[] args)
{
IIndex<Rectangle> rectangles =
RectangleCollection.GetRectangles();
IIndex<Shape> shapes = rectangles; for (int i = 0; i < shapes.Count; i++)
{
Console.WriteLine(shapes[i]);
} Console.WriteLine("##########"); IDisplay<Shape> shapeDisplay = new ShapeDisplay();
IDisplay<Rectangle> rectangleDisplay = shapeDisplay;
rectangleDisplay.Show(rectangles[0]); Console.WriteLine("########## .net 框架中的示例 ###########"); /*
public interface IEnumerable<out T> : IEnumerable //协变 public delegate void Action<in T>( //逆变
T obj
)
*/ Action<Shape> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Rectangle> d = b; //逆变:Shape是Rectangle的超类型,而Action<Rectangle>却是Action<Shape>的超类型
d(new Rectangle());
b(new Shape()); Console.ReadKey();
}
} public interface IDisplay<in T> //该类型参数是逆变。即可以使用指定的类型或派生程度更低的类型。
{
void Show(T item);
} public class ShapeDisplay : IDisplay<Shape>
{
public void Show(Shape s)
{
Console.WriteLine("{0} Width: {1}, Height: {2}", s.GetType().Name,
s.Width, s.Height);
}
} }
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)? 在变成语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数,泛型,返回值)替换或交换的特性,简单的说,协变是 ...
随机推荐
- [BZOJ4897][THUSC2016]成绩单(DP)
4897: [Thu Summer Camp2016]成绩单 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 220 Solved: 132[Subm ...
- 【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家
裸题,树状数组区间修改+单点查询.当然要稍微讨论一下链的左右端点是否修改的情况咯. #include<cstdio> #include<algorithm> #include& ...
- 【最小瓶颈生成树】【最小生成树】【kruscal】bzoj1083 [SCOI2005]繁忙的都市
本意是求最小瓶颈生成树,但是我们可以证明:最小生成树也是最小瓶颈生成树(其实我不会).数据范围很小,暴力kruscal即可. #include<cstdio> #include<al ...
- 【并查集+离散化】BZOJ4195- [Noi2015]程序自动分析
[题目大意] 在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足. 考虑一个约束满足问题的简化版本:假设x1,x2,x3,…代表程序中出现的变量,给定n个形如xi=xj或xi≠xj的 ...
- Network Connection Lost When Windows 8 Goes To Sleep
http://www.kapilarya.com/fix-network-connection-lost-when-windows-8-goes-to-sleep http://superuser.c ...
- 全局流水ID号生成的几种方法
这个问题源自于,我想找一个分布式下的ID生成器. 这个最简单的方案是,数据库自增ID.为啥不用咧?有这么几点原因,一是,会依赖于数据库的具体实现,比如,mysql有自增,oracle没有,得用序列, ...
- 微信开发之自定义菜单--weixin-java-tools
一.前言 平时在开发微信的过程中,肯定会设计到微信的相关菜单的使用,这次就和大家介绍下如何使用weixin-java-tools来管理菜单 二.自定义菜单分类 1.click:点击推事件用户点击cli ...
- [转载]使用32位64位交叉编码混淆来打败静态和动态分析工具 - wildsator
0×00 摘要 混淆是一种能增加二进制分析和逆向工程难度与成本的常用技术.主流的混淆技术都是着眼于使用与目标CPU相同的机器代码,在相同的处理器模式下,隐藏代码并进行控制.本文中引入了一种新的混淆方法 ...
- 基于CentOS与VmwareStation10搭建Oracle11G RAC 64集群环境:3.安装Oracle RAC-3.2.安装 cvuqdisk 软件包
3.2.安装 cvuqdisk 软件包 3.2.1. 准备Oracle Grid安装包 上传Grid .Oracle 安装文件: sftp> put E:\Software\linux.x64 ...
- Java线程:概念与原理(转)
一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程.比如在 ...