C#4.0中有一个新特性:协变与逆变。可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的。

协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变。什么?泛型的参数还能声明?对,如果有了参数的声明,则该泛型接口或者委托称为“变体”。

List<汽车> 一群汽车 = new List<汽车>();
List<车子> 一群车子 = 一群汽车;

显然,上面那段代码是会报错的, 虽然汽车继承于车子,可以隐士转换为车子,但是List<汽车>并不继承于List<车子>,所以上面的转换,是行不通的。

IEnumerable<汽车> 一群汽车 = new List<汽车>();
IEnumerable<车子> 一群车子 = 一群汽车;

然而这样却是可以的。那么IEnumerable接口有什么不同呢,我们且看编译器的提示:

我们可以看到,泛型参数的,用了一个“out”关键字作为声明。看来,关键是这个在起作用了。

 “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。 

 “逆变”则是指能够使用派生程度更小的类型。逆变,逆于常规的变。

协变和逆变,使用“out”,和“in”两个关键字。但是只能用在接口和委托上面,对泛型的类型进行声明

当声明为“out”时,代表它是用来返回的,只能作为结果返回,中途不能更改。

当声明为"in"时,代表它是用来输入的,只能作为参数输入,不能被返回。

回到上面的例子,正因为“IEnumerable”接口声明了out,所以,代表参数T只能被返回,中途不会被修改,所以,IEnumerable<车子> 一群车子 = 一群汽车;  这样的强制转换

是合法的,IL中实际上是作了强制转换的。

IEnumerable是NET中自带的,其余还有如下接口和委托:

接口:           
IQueryable<out T>
IEnumerator<out T>
IGrouping<out TKey,out TElement>
IComparer<in T>
IEqualityComparer<in T>
IComparable<in T>
委托:
System.Action<in T>
System.Func<Out Tresult>
Predicate<in T>
Comparison<in T>
Converter<in TInput,out TOutput>

此外,我们自己定义泛型接口的时候也可以使用协变和逆变,我们不妨来看一个示例,来体现协变的特征

    interface 接口<out T>
{
T 属性 { get; set; }
}

我定义一个接口,一个具有get和set访问器的属性,然而,编译是报错的,提示:变体无效: 类型参数“T”必须为对于“test.接口<T>.属性”有效的 固定式。“T”为 协变。

正因为我声明了T为协变,所以,T只能被返回,不允许被修改,所以,如果去掉“set”访问器,才可以编译通过。

同样,如果我在“接口”中声明一个方法

void 方法(T t);

同样是会报错的,T被声明了协变,“方法(T t)”的存在就不可取。

class Program
{
static void Main(string[] args)
{
接口<汽车> 一群汽车 = new 类<汽车>();
接口<车子> 一群车子 = 一群汽车;
}
}
interface 接口<out T>
{
T 属性
{
get;
}
}
class 类<T> : 接口<T>
{
public T 属性
{
get { return default(T); }
}
}

上面的代码是可以编译通过的,因为泛型接口“接口”声明了协变,所以“接口<车子> 一群车子 = 一群汽车;”是可以强制转换成功的,看吧,我们自己声明的同样可以实现目的。

如果我把以上的代码,把“out”改成“in”呢? 显然不行,因为声明“in”规定了T不能被返回,编译无法通过的。

然而下面的代码是正确的:

    interface 接口<in T>
{
void 方法(T t);
}
class 类<T> : 接口<T>
{
public void 方法(T t)
{ }
}

声明“in”不允许被返回,但是可以进行更改。

接着看:

        static void Main(string[] args)
{
接口<车子> 一群车子 = new 类<车子>();
接口<汽车> 一群汽车 = 一群车子;
}

啊,这怎么也可以啊,“车子”是父类,“汽车”是子类,汽车转换为车子正常,车子转换为汽车,这样也行?

其实“车子”也好,“汽车”也好,在这里都只是泛型参数,并不是他们俩之间的转换,这个基础的概念必须明白,别绕进去了。
这就是逆变。因为“接口”声明了“in”关键字,声明为逆变,让参数去接受一个相对更“弱“的类型,其实是让一个参数的类型,更加具体化,更明确化的一个过程。

  

C#4.0泛型的协变,逆变深入剖析的更多相关文章

  1. 解读经典《C#高级编程》最全泛型协变逆变解读 页127-131.章4

    前言 本篇继续讲解泛型.上一篇讲解了泛型类的定义细节.本篇继续讲解泛型接口. 泛型接口 使用泛型可定义接口,即在接口中定义的方法可以带泛型参数.然后由继承接口的类实现泛型方法.用法和继承泛型类基本没有 ...

  2. C#中泛型方法与泛型接口 C#泛型接口 List<IAll> arssr = new List<IAll>(); interface IPerson<T> c# List<接口>小技巧 泛型接口协变逆变的几个问题

    http://blog.csdn.net/aladdinty/article/details/3486532 using System; using System.Collections.Generi ...

  3. 编写高质量代码改善C#程序的157个建议——建议45:为泛型类型参数指定逆变

    建议45:为泛型类型参数指定逆变 逆变是指方法的参数可以是委托或者泛型接口的参数类型的基类.FCL4.0中支持逆变的常用委托有: Func<int T,out TResult> Predi ...

  4. C#的in/out关键字与协变逆变

    C#提供了一组关键字in&out,在泛型接口和泛型委托中,若不使用关键字修饰类型参数T,则该类型参数是不可变的(即不允许协变/逆变转换),若使用in修饰类型参数T,保证"只将T用于输 ...

  5. Kotlin泛型与协变及逆变原理剖析

    在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...

  6. Programming In Scala笔记-第十九章、类型参数,协变逆变,上界下界

    本章主要讲Scala中的类型参数化.本章主要分成三个部分,第一部分实现一个函数式队列的数据结构,第二部分实现该结构的内部细节,最后一个部分解释其中的关键知识点.接下来的实例中将该函数式队列命名为Que ...

  7. java协变逆变,PECS

    public static void main(String[] args) { // Object <- Fruit <- Apple <- RedApple System.out ...

  8. 协变 & 逆变

    都跟里氏替换原则有关. 协变:你可以用一个子类对象去替换相应的一个父类对象,这是完全符合里氏替换原则的,和协(谐)的变.如:用Swan替换Bird. 逆变:你可以用一个父类对象去替换相应的一个子类对象 ...

  9. C#核心语法讲解-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

随机推荐

  1. [Spring]IoC容器之进击的注解

    先啰嗦两句: 第一次在博客园使用markdown编辑,感觉渲染样式差强人意,还是github的样式比较顺眼. 概述 Spring2.5 引入了注解. 于是,一个问题产生了:使用注解方式注入 JavaB ...

  2. 前端框架 EasyUI (0) 重新温习(序言)

    几年前,参与过一个项目.那算是一个小型的信息管理系统,BS 结构的,前端用的是基于 jQuery 的 EasyUI 框架. 我进 Team 的时候,项目已经进入开发阶段半个多月了.听说整个项目的框架是 ...

  3. 流程开发Activiti 与SpringMVC整合实例

    流程(Activiti) 流程是完成一系列有序动作的概述.每一个节点动作的结果将对后面的具体操作步骤产生影响.信息化系统中流程的功能完全等同于纸上办公的层级审批,尤其在oa系统中各类电子流提现较为明显 ...

  4. 【Machine Learning】决策树案例:基于python的商品购买能力预测系统

    决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...

  5. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  6. ASP.NET Core 中文文档 第四章 MVC(4.1)Controllers, Actions 和 Action Results

    原文:Controllers, Actions, and Action Results 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:许登洋(Seay) Action 和 acti ...

  7. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  8. html中table边框属性

    1.向右(横向)合并: <td colspan="5"><span>后台管理系统</span></td> 2.向下(纵向)合并: & ...

  9. 手机web如何实现多平台分享

    话说App一般都带有分享到社交平台的入口,web网页的分享也有很不错的框架,但是随着HTML5的不断发展,手机web页面越来越多的进入到我们的生活中,那如何在我们的手机上完成分享呢?话说各大分享平台都 ...

  10. SuperMap-iServer-单点登录功能验证(CAS)

    SuperMap-iServer-单点登录功能验证(CAS) 1.测试目的: 验证SuperMap-iServer使用CAS单点登录的功能是否正常. 2.测试环境: SuperMap-iServer8 ...