13.3.1 可变性的种类:协变性和逆变性

1. 协变性:从API返回的值

协变性用于向调用者返回某项操作的值。例如一个简单的表示工厂模式的泛型接口,它只包含一个方法 CreateInstanse ,返回适当类型的实例。代码如下

1     public interface IFactory<T>
2     {
3         T CreateInstance();
4     }

现在, T 在接口中只出现了一次(除了在签名中),它仅作为返回值使用,即方法的输出。
这意味着可以将特定类型的工厂视为更一般类型的工厂。如在现实世界里,你可以将比萨工厂视为食品工厂。

2. 逆变性:传入API的值

逆变性则相反。它指的是调用者向API传入值,即API是在消费值,而不是产生值。
我们来想象另一个简单的接口——它可以向控制台打印特定的文档类型。

同样,它也只有一个方法Print :

1     public interface IPrettyPrinter<T>
2     {
3         void Print(T document);
4     }

这次 T 只作为参数出现在了接口的输入位置。
具体而言,如果我们实现了 IPrettyPrinter<SourceCode> ,就可以将其当作 IPrettyPrinter<CSharpCode> 来使用。

3. 不变性:双向传递的值

如果协变性适用于仅从API输出值的情况,而逆变性用于仅向API输入值的情况,那么如果值双向传递会如何呢?
简而言之,什么也不会发生。这种类型是不变体(invariant)。
下面的接口表示可以对数据类型进行序列化和反序列化的类型:

1     public interface IStorage<T>
2     {
3         byte[] Serialize(T value);
4         T Deserialize(byte[] data);
5     }

这时,如果存在一个具有特定类型 T 的 IStorage<T> 实例,我们不能将其视为该接口更具体或更一般类型的实现。
如果以协变的方式使用(如将 IStorage<Customer> 视为 IStorage-<Person> ),则可能在调用 Serialize 时传入一个无法处理的对象。
类似地,如果以逆变的方式使用,则可能在反序列化数据时得到一个预料之外的类型。
如果有助于理解的话,可以将不变性看成 ref 参数:按引用传递变量,其类型必须与参数本身的类型完全一致,因为值被传入了方法内部,并且同样被高效地传出。

原文地址:https://www.cnblogs.com/kikyoqiang/p/10099549.html