上节讲到了泛型,这节延申一下,讲一下变体。

变体(variance)是协变(convariance)和抗变(也说逆变contravariance)的统称。这个概念在.net 4中引入,在.net 2.0中就可以使用,但是比较麻烦,.net 4将这一概念封装成了特性。

讲变体之前,我们先来复习一下多态性。请看如下代码:

class Animals
{
public virtual void Say(){}
}

class Cat : Animals
{
public override void Say()
{
Console.WriteLine("小猫喵喵叫");
}
}

class Dog : Animals
{
public override void Say()
{
Console.WriteLine("小狗汪汪汪");
}
}

interface IAnimals<out T> where T :Animals, new()
{
void InvokeSay();
}
interface IAnimalsType<in T> where T : Animals
{
Type GetType();
}

class AnimalsType<T>:IAnimalsType<T> where T : Animals
{
public Type GetType()
{
return typeof(T);
}
}

class AnimalsAdmin<T> : IAnimals<T> where T : Animals,new()
{
//此处涉及到反射,不清楚反射的读者请留意后期文章
//此处只需知道调用了传入实例类的Say()方法即可
public void InvokeSay()
{
Type type = typeof(T);
T t = new T();
MethodInfo Say = type.GetMethod("Say");
Say.Invoke(t, null);
}
}

有一父类Animals,Cat和Dog继承此类,根据多态性,以下代码是可行的:

Animals cat = new Cat();
Animals dog = new Dog();

有两个接口,分别由两个类继承,先分析其中一个类和接口,下面的代码是编译不过的:

IAnimals<Animals> animals;
animals=new AnimalsAdmin<Dog>();//父类是IAnimals<Dog>
animals= new AnimalsAdmin<Cat>();//父类是IAnimals<Cat>

以上转换,在多态性中看似是可以的,但其实这样的转换不属于多态。多态性是基于类的继承,若两个类没有继承关系,何谈多态,AnimalsAdmin<Dog>和AnimalsAdmin<Cat>的父类和IAnimals<Animals>是平行类型关系,没有继承关系。只有以下代码是可行的:

IAnimals<Animals> animals;
animals=new AnimalsAdmin<Animals>();

而变体,让这样的转换变的可行。

协变:

为了建立他们之间的继承关系,接口IAnimals<T>的类型需要设置为协变,有了协变类型,AnimalsAdmin<Dog>,AnimalsAdmin<Cat>这两个类和IAnimals<Animals>就建立了继承关系(这一点比多态性稍微复杂一些)。那如何设为协变类型呢:

interface IAnimals<out T> where T :Animals, new()
{
void InvokeSay();
}

T前加关键字out即可。我们来看一下运行结果:

微软的API中也有很多协变的例子:例如IEnumerable泛型接口就是协变性的。

抗变:

    了解了协变,那么对于抗变,小伙伴们也能猜出是什么意思,协变是向上转换,请分析另一对接口和类,看如下代码:

AnimalsAdmin<Cat> cAdmin = new AnimalsAdmin<Cat>();
Type type = cAdmin.GetAnimalType(new AnimalsType<Animals>());

     没有抗变,以上代码显然是不可行的,因为cAdmin.GetAnimalType的参数需要一个AnimalsType<Cat>类型的,为了使这种转换可行,需将IAnimalsType<T>接口设置为抗变类型:

interface IAnimalsType<in T> where T :Animals
{
Type GetAnimalType();
}

在T前加关键字in,我们来看一下运行结果:

 抗变在.net Framework中有很多用处,IComparable泛型接口就是抗变类型。

通过变体,我们在面向泛型接口编程的时候,就可以借助多态性很灵活的编码。最后注意两点:设置为协变类型的T,只能用作返回类型和属性get访问器的类型,而设置为抗变类型的T只能用作方法的参数。

 本节源码文件位于:

  https://github.com/Chunlei-Su/PublicDemo/tree/master/C%23/C%23Senior/Variance(%E5%8F%98%E4%BD%93)

这是我的公众号二维码,获取最新文章,请关注此号

C# 变体(variance)的更多相关文章

  1. 自然语言19.1_Lemmatizing with NLTK(单词变体还原)

    QQ:231469242 欢迎喜欢nltk朋友交流 https://www.pythonprogramming.net/lemmatizing-nltk-tutorial/?completed=/na ...

  2. 【Visual Lisp】变体与安全数组

    (vlax-make-variant) ;;创建一个未初始化的变体 ;;01.整型值变体(setq myvar (vlax-make-variant 10)) ;;创建整型值变体,返回 #<va ...

  3. labview 变体数据类型

    变体数据类型是LabVIEW中多种数据类型的容器.将其它数据转换为变体时,变体将存储数据和数据的原始类型,保证日后可将变体数据反向转换. 例如,如将字符串数据转换为变体,变体将存储字符串的文本,以及说 ...

  4. Bootstrap 标签的变体 实例样式

    Bootstrap 标签样式,代码如下: <!DOCTYPE html> <html> <head> <title>Bootstrap 实例 - 标签的 ...

  5. Odoo / PS Cloud12版本中,产品变体功能如何使用

    场景: 产品:陶瓷马克杯 产品颜色变体:红色.蓝色.白色 产品尺寸变体:10CM.12CM.15CM 每个变体都有不同价格维度 odoo / PS Cloud 专业实施开发 EMAIL:1715860 ...

  6. 二叉查找树及B-树、B+树、B*树变体

    动态查找树主要有二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree), 红黑树 (Red-Black Tree ), 都是典型的 ...

  7. (转) 干货 | 图解LSTM神经网络架构及其11种变体(附论文)

    干货 | 图解LSTM神经网络架构及其11种变体(附论文) 2016-10-02 机器之心 选自FastML 作者:Zygmunt Z. 机器之心编译  参与:老红.李亚洲 就像雨季后非洲大草原许多野 ...

  8. 完全图解RNN、RNN变体、Seq2Seq、Attention机制

    完全图解RNN.RNN变体.Seq2Seq.Attention机制 本文主要是利用图片的形式,详细地介绍了经典的RNN.RNN几个重要变体,以及Seq2Seq模型.Attention机制.希望这篇文章 ...

  9. Delphi 变体数组 Dataset Locate 查找定位

    Format 函数 Delphi 支持“开参数”和动态数组,变体数组,使用时的语法类似 Delphi 中的集合:采用两个方括号把不同类型的变量括起来(这太方便了啊),也可以采用声明一个 TVarRec ...

随机推荐

  1. POJ_2387 Til the Cows Come Hom 【最短路】

    一.题目 POJ2387 二.分析 Bellman-Ford算法 该算法是求单源最短路的,核心思想就是不断去更新到起点的最短距离,更新的前提是没有负边.如果有负边需要手动控制循环次数. Dijkstr ...

  2. [Python] 波士顿房价的7种模型(线性拟合、二次多项式、Ridge、Lasso、SVM、决策树、随机森林)的训练效果对比

    目录 1. 载入数据 列解释Columns: 2. 数据分析 2.1 预处理 2.2 可视化 3. 训练模型 3.1 线性拟合 3.2 多项式回归(二次) 3.3 脊回归(Ridge Regressi ...

  3. 攻防世界 reverse elrond32

    tinyctf-2014 elrond32 1 int __cdecl main(int a1, char **arg_input) 2 { 3 if ( a1 > 1 && c ...

  4. JavaScript中函数防抖、节流

    码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14565642.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...

  5. MySQL Order BY 排序过程

    MySQL 在进行 Order By 操作排序时,通常有两种排序方式: 全字段排序 Row_id 排序 MySQL 中每个线程在执行排序时,都会被分配一块区域 - sort buffer,它的大小通过 ...

  6. java例题_41 利用递归给猴子分桃

    1 /*41 [程序 41 猴子分桃] 2 题目:海滩上有一堆桃子,五只猴子来分.第一只猴子把这堆桃子平均分为五份,多了一个,这只猴子把 3 多的一个扔入海中,拿走了一份.第二只猴子把剩下的桃子又平均 ...

  7. CodeForces CF862E题解

    \(Part\ 1:\) 我们发现每次修改动的是\(a\)串,所以对于这个答案的公式,\(b_{i+j}\)的部分是可以求出来的.所以我们可以把公式改成如下所示: \(f(j)=|\sum_{i=1} ...

  8. 面试题-你听过TCP Fast Open (TFO/TCP快速打开)吗?能解释一下吗?

    TCP Fast Open (TFO/TCP快速打开) TCP快速打开(TCP Fast Open,TFO)是什么? TCP快速打开(TCP Fast Open,TFO)是对TCP的一种简化握手手续的 ...

  9. 一本关于HTTP的恋爱日记

    1991年 8月 我叫客户端,英文名字 client. 她叫服务端,英文名字 server. 这一年,我们出生了. 是的,我们都是90后. 我爱她,可是她却远在天边. 为了和她可以互诉衷肠,我同时发明 ...

  10. Fundamentals of Power Electronics 目录

    Fundamentals of Power Electronics Translated By Siwei Yang (前六章翻译自Edition 2,后面部分翻译自Edition 3) Part I ...