c# 协变与抗变

协变与抗变

1. 背景

一个简单的例子,

public class Sharp
{
   
   
}

public class Rectange : Sharp
{
   
   
}

上面定义了两个简单的类,一个是图形类,一个是矩形类;它们之间有简单的继承关系:矩形是图形的一种。

接下来是常见的一种里氏替换写法:

Sharp sharp = new Rectange();

“子类引用可以直接转化成父类引用”,即 Rectange 类和 Sharp 类之间存在一种安全的隐式转换。

既然Rectange类和Sharp类之间存在一种安全的隐式转换,那数组Rectange[]和Sharp[]之间是否也存在这种安全的隐式转换呢?

这种将原本类型上存在的类型转换映射到他们的数组类型(或接口、委托等其它类型)上的能力,可称为“可变性(Variance)”。

在.NET中,唯一允许可变性的类型转换就是由继承关系带来的“子类引用->父类引用”转换,就是上面例子的写法。

再看下面这种写法:

Sharp[] sharps=new Rectange[3];

编译通过,这说明 Rectange[] 和 Sharp[] 之间存在安全的隐式转换。

2. 定义

2.1 协变定义

在具有可变性的条件下,与原始类型转换方向相同的可变性称作协变(covariant)。

2.2 抗变定义

在具有可变性的条件下,与原始类型转换方向相反的可变性称作抗变(contravariant),也称逆变。

可变性远远不只是针对映射到数组的能力,也有映射其它集合的能力如List.
很多人会问,说了这么多,到底这个协变或者抗变有什么实际价值?

举个例子,在.net 4.0之前可以这么写:

Sharp sharp = new Rectange();

但是却不能这么写:

IEnumerable<Sharp> sharps = new List<Rectange>();

4.0之后,可以允许按上面的写法了,因为泛型接口 IEnumerable<T> 被声明成如下:

public interface IEnumerable<out T> : IEnumerable

为什么接口参数类型前加了个 out 关键字就可以安全转换了?
因为在接口类型参数前加上修饰关键字 in 和 out 可以表示该类型参数支持抗变和协变,CLR 会自动在安全范围内进行转换。

目前 C# 支持协变和抗变的有两种类型:泛型接口泛型委托

由于它俩机制差不多,这里仅记录一下泛型接口的协变和抗变。

3. 泛型接口中的协变和抗变

定义一个泛型接口:

public interface ICovariant<T>
{
   
   
}

两个类各自继承一下该接口:

public class Sharp : ICovariant<Sharp>
{
   
   
}

public class Rectange : Sharp, ICovariant<Rectange>
{
   
   
}

测试代码:

ICovariant<Sharp> isharp = new Sharp();
ICovariant<Rectange> irect = new Rectange()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值