「共変」で何ができる?
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
)
これを行うとき(古典的な共同バリアントの割り当て):
//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
より多くのメンバーがいます)。
これを行うと(古典的なコントラバリアントの割り当て):
//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)
それが反対の場合(それが共同バリアントであるかのように):
//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バリアント」タイプを入力パラメーターとして使用できない理由です。
両方に参加する
では、2つの「できない」を一緒に追加するとどうなりますか?
- これはできません+それはできません=何もできません
あなたは何ができますか?
私はこれをテストしていません(まだ...これを行う理由があるかどうかを考えています)が、いくつかの制限があることを知っていれば、問題ないようです。
目的の型のみを出力するメソッドと、それを入力パラメーターとしてのみ受け取るメソッドを明確に分離している場合は、2つのインターフェースを使用してクラスを実装できます。
in
出力されないメソッドのみを使用および持つ1つのインターフェースT
- 入力を
out
受け取らないメソッドのみを持つ別のインターフェースT
必要な状況で各インターフェイスを使用しますが、相互に割り当てようとしないでください。