C# 变体(variance)
上节讲到了泛型,这节延申一下,讲一下变体。
变体(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)的更多相关文章
- 自然语言19.1_Lemmatizing with NLTK(单词变体还原)
QQ:231469242 欢迎喜欢nltk朋友交流 https://www.pythonprogramming.net/lemmatizing-nltk-tutorial/?completed=/na ...
- 【Visual Lisp】变体与安全数组
(vlax-make-variant) ;;创建一个未初始化的变体 ;;01.整型值变体(setq myvar (vlax-make-variant 10)) ;;创建整型值变体,返回 #<va ...
- labview 变体数据类型
变体数据类型是LabVIEW中多种数据类型的容器.将其它数据转换为变体时,变体将存储数据和数据的原始类型,保证日后可将变体数据反向转换. 例如,如将字符串数据转换为变体,变体将存储字符串的文本,以及说 ...
- Bootstrap 标签的变体 实例样式
Bootstrap 标签样式,代码如下: <!DOCTYPE html> <html> <head> <title>Bootstrap 实例 - 标签的 ...
- Odoo / PS Cloud12版本中,产品变体功能如何使用
场景: 产品:陶瓷马克杯 产品颜色变体:红色.蓝色.白色 产品尺寸变体:10CM.12CM.15CM 每个变体都有不同价格维度 odoo / PS Cloud 专业实施开发 EMAIL:1715860 ...
- 二叉查找树及B-树、B+树、B*树变体
动态查找树主要有二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree), 红黑树 (Red-Black Tree ), 都是典型的 ...
- (转) 干货 | 图解LSTM神经网络架构及其11种变体(附论文)
干货 | 图解LSTM神经网络架构及其11种变体(附论文) 2016-10-02 机器之心 选自FastML 作者:Zygmunt Z. 机器之心编译 参与:老红.李亚洲 就像雨季后非洲大草原许多野 ...
- 完全图解RNN、RNN变体、Seq2Seq、Attention机制
完全图解RNN.RNN变体.Seq2Seq.Attention机制 本文主要是利用图片的形式,详细地介绍了经典的RNN.RNN几个重要变体,以及Seq2Seq模型.Attention机制.希望这篇文章 ...
- Delphi 变体数组 Dataset Locate 查找定位
Format 函数 Delphi 支持“开参数”和动态数组,变体数组,使用时的语法类似 Delphi 中的集合:采用两个方括号把不同类型的变量括起来(这太方便了啊),也可以采用声明一个 TVarRec ...
随机推荐
- POJ_2387 Til the Cows Come Hom 【最短路】
一.题目 POJ2387 二.分析 Bellman-Ford算法 该算法是求单源最短路的,核心思想就是不断去更新到起点的最短距离,更新的前提是没有负边.如果有负边需要手动控制循环次数. Dijkstr ...
- [Python] 波士顿房价的7种模型(线性拟合、二次多项式、Ridge、Lasso、SVM、决策树、随机森林)的训练效果对比
目录 1. 载入数据 列解释Columns: 2. 数据分析 2.1 预处理 2.2 可视化 3. 训练模型 3.1 线性拟合 3.2 多项式回归(二次) 3.3 脊回归(Ridge Regressi ...
- 攻防世界 reverse elrond32
tinyctf-2014 elrond32 1 int __cdecl main(int a1, char **arg_input) 2 { 3 if ( a1 > 1 && c ...
- JavaScript中函数防抖、节流
码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14565642.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...
- MySQL Order BY 排序过程
MySQL 在进行 Order By 操作排序时,通常有两种排序方式: 全字段排序 Row_id 排序 MySQL 中每个线程在执行排序时,都会被分配一块区域 - sort buffer,它的大小通过 ...
- java例题_41 利用递归给猴子分桃
1 /*41 [程序 41 猴子分桃] 2 题目:海滩上有一堆桃子,五只猴子来分.第一只猴子把这堆桃子平均分为五份,多了一个,这只猴子把 3 多的一个扔入海中,拿走了一份.第二只猴子把剩下的桃子又平均 ...
- CodeForces CF862E题解
\(Part\ 1:\) 我们发现每次修改动的是\(a\)串,所以对于这个答案的公式,\(b_{i+j}\)的部分是可以求出来的.所以我们可以把公式改成如下所示: \(f(j)=|\sum_{i=1} ...
- 面试题-你听过TCP Fast Open (TFO/TCP快速打开)吗?能解释一下吗?
TCP Fast Open (TFO/TCP快速打开) TCP快速打开(TCP Fast Open,TFO)是什么? TCP快速打开(TCP Fast Open,TFO)是对TCP的一种简化握手手续的 ...
- 一本关于HTTP的恋爱日记
1991年 8月 我叫客户端,英文名字 client. 她叫服务端,英文名字 server. 这一年,我们出生了. 是的,我们都是90后. 我爱她,可是她却远在天边. 为了和她可以互诉衷肠,我同时发明 ...
- Fundamentals of Power Electronics 目录
Fundamentals of Power Electronics Translated By Siwei Yang (前六章翻译自Edition 2,后面部分翻译自Edition 3) Part I ...