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