你可以用“协变”做什么?
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
!!
- 那么这是不可能的。
这就是为什么“对立变体”类型不能用作输出类型 的原因
你可以用“逆变器”做什么?
遵循上面相同的想法,逆变使用修饰符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)
也可以使用方法(它只看的TSmall
部分TBig
)
- 由于
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)
找不到TBig
方法TSmall
!!!并且不能保证这smallInstance
甚至是TBig
转换的。
- 由于
smallAnalyser
被分配bigAnalyser
:
- 调用
smallAnalyser.isInstanceCool(smallInstance)
将尝试TBig
在实例中查找方法
- 它可能找不到
TBig
方法,因为这smallInstance
可能不是一个TBig
实例。
这就是为什么“ co variant”类型不能用作输入参数的原因
加入两者
现在,当您将两个“不能”加在一起时会发生什么?
你能做什么?
我还没有测试过这个(还......我在想我是否有理由这样做),但它似乎没问题,只要你知道你会有一些限制。
如果您将仅输出所需类型的方法和仅将其作为输入参数的方法明确分离,则可以使用两个接口来实现您的类。
- 一个接口使用
in
并且只有不输出的方法T
- 另一个接口
out
只使用不T
作为输入的方法
在需要的情况下使用每个接口,但不要试图将一个分配给另一个。