你可以用“协变”做什么?
Covariant 使用修饰符out,表示该类型可以是方法的输出,但不能是输入参数。
假设你有这些类和接口:
interface ICanOutput<out T> { T getAnInstance(); }
class Outputter<T> : ICanOutput<T>
{
public T getAnInstance() { return someTInstance; }
}
现在假设你有 TBig 继承 TSmall 的类型。这意味着TBig 实例也始终是TSmall 实例;但TSmall 实例并不总是TBig 实例。 (选择名称是为了便于将TSmall 放入TBig 中进行可视化)
当你这样做时(一个经典的co变体赋值):
//a real instance that outputs TBig
Outputter<TBig> bigOutputter = new Outputter<TBig>();
//just a view of bigOutputter
ICanOutput<TSmall> smallOutputter = bigOutputter;
-
bigOutputter.getAnInstance() 将返回 TBig
- 因为
smallOutputter 被分配了bigOutputter:
- 在内部,
smallOutputter.getAnInstance() 将返回 TBig
- 并且
TBig可以转换为TSmall
- 转换完成,输出为
TSmall。
如果它是相反的(好像它是相反变体):
//a real instance that outputs TSmall
Outputter<TSmall> smallOutputter = new Outputter<TSmall>();
//just a view of smallOutputter
ICanOutput<TBig> bigOutputter = smallOutputter;
-
smallOutputter.getAnInstance() 将返回 TSmall
- 因为
bigOutputter 被分配了smallOutputter:
- 在内部,
bigOutputter.getAnInstance() 将返回 TSmall
- 但是
TSmall不能转换成TBig!!
- 这是不可能的。
这就是为什么“contravariant”类型不能用作输出类型
你可以用“逆变”做什么?
同上思路,逆变使用修饰符in,表示类型可以是方法的输入参数,但不能作为输出参数。
假设你有这些类和接口:
interface ICanInput<in T> { bool isInstanceCool(T instance); }
class Analyser<T> : ICanInput<T>
{
bool isInstanceCool(T instance) { return instance.amICool(); }
}
再次,假设类型TBig 继承TSmall。这意味着TBig 可以做TSmall 所做的所有事情(它拥有所有TSmall 成员等等)。但是TSmall 不能做TBig 所做的一切(TBig 有更多成员)。
当你这样做时(经典的contra变体赋值):
//a real instance that can use TSmall methods
Analyser<TSmall> smallAnalyser = new Analyser<TSmall>();
//this means that TSmall implements amICool
//just a view of smallAnalyser
ICanInput<TBig> bigAnalyser = smallAnalyser;
-
smallAnalyser.isInstanceCool:
-
smallAnalyser.isInstanceCool(smallInstance)可以使用smallInstance中的方法
-
smallAnalyser.isInstanceCool(bigInstance) 也可以使用方法(它只查看TBig 的TSmall 部分)
- 因为
bigAnalyser 被分配了smallAnalyer:
- 完全可以拨打
bigAnalyser.isInstanceCool(bigInstance)
如果是相反的(好像它是co变体):
//a real instance that can use TBig methods
Analyser<TBig> bigAnalyser = new Analyser<TBig>();
//this means that TBig has amICool, but not necessarily that TSmall has it
//just a view of bigAnalyser
ICanInput<TSmall> smallAnalyser = bigAnalyser;
- 对于
bigAnalyser.isInstanceCool:
-
bigAnalyser.isInstanceCool(bigInstance)可以使用bigInstance中的方法
- 但是
bigAnalyser.isInstanceCool(smallInstance) 在TSmall 中找不到TBig 方法!!!并且不能保证这个 smallInstance 甚至是 TBig 转换的。
- 因为
smallAnalyser 被分配了bigAnalyser:
- 调用
smallAnalyser.isInstanceCool(smallInstance) 将尝试在实例中查找TBig 方法
- 它可能找不到
TBig 方法,因为这个smallInstance 可能不是TBig 实例。
这就是为什么“covariant”类型不能用作输入参数
同时加入
现在,当您将两个“不能”加在一起时会发生什么?
你能做什么?
我还没有测试过这个(还......我在想我是否有理由这样做),但它似乎没问题,只要你知道你会有一些限制。
如果您将仅输出所需类型的方法和仅将其作为输入参数的方法明确分离,则可以使用两个接口来实现您的类。
- 一个接口使用
in,并且只有不输出T的方法
- 另一个使用
out 的接口只有不以T 作为输入的方法
在需要的情况下使用每个接口,但不要试图将一个分配给另一个。