Action<Action<T>>
わかりましたので、まず、それが共変であると言う意味を明確にしましょう。次のステートメントが成り立つことを意味します。
- 参照型 X のオブジェクトを参照型 Y の変数に割り当てることができる場合、型 のオブジェクトを参照型 の変数に
Action<Action<X>>
割り当てることができますAction<Action<Y>>
。
さて、それが機能するかどうか見てみましょう。クラスがFish
ありAnimal
、明らかな継承があるとします。
static void DoSomething(Fish fish)
{
fish.Swim();
}
static void Meta(Action<Fish> action)
{
action(new Fish());
}
...
Action<Action<Fish>> aaf = Meta;
Action<Fish> af = DoSomething;
aaf(af);
それは何をしますか?DoSomething へのデリゲートを Meta に渡します。これにより新しい魚が作成され、DoSomething によって魚が泳がされます。問題ない。
ここまでは順調ですね。問題は、なぜこれが合法である必要があるのかということです。
Action<Action<Animal>> aaa = aaf;
では、許可するとどうなるか見てみましょう。
aaa(af);
何が起こるのですか?明らかに前と同じです。
ここで何か問題を起こすことはできますか? 以外のものを渡すとどうなるでしょうか。そうすると に渡されることを思い出してaf
ください。aaa
Meta
さて、何に渡すことができaaa
ますか?任意Action<Animal>
:
aaa( (Animal animal) => { animal.Feed(); } );
そして、どうなりますか?Meta
新しい魚でデリゲートを呼び出す にデリゲートを渡し、魚に餌を与えます。問題ない。
T はまだ出力位置にありません。誰かがこの背後にある理由/ロジックを説明しようとしてくれれば、本当に感謝しています.
「入力/出力」位置はニーモニックです。共変型は出力位置に T を持つ傾向があり、反変型は入力位置に T を持つ傾向がありますが、それは普遍的に当てはまるわけではありません。ほとんどの場合、これが正しいため、キーワードとしてin
andを選択しました。out
しかし、本当に重要なことは、型は型安全な方法でしか使用できないということです。
ここで、別の方法を考えてみましょう。共分散は矢印の方向を保持します。矢印string --> object
を描くと、「同じ」矢印を描くことができますIEnumerable<string> --> IEnumerable<object>
。反変性は、矢印の方向を逆にします。ここで矢印はX --> Y
、X への参照を Y 型の変数に格納できることを意味します。
Fish --> Animal
Action<Fish> <-- Action<Animal>
Action<Action<Fish>> --> Action<Action<Animal>>
Action<Action<Action<Fish>>> <-- Action<Action<Action<Animal>>>
...
それがどのように機能するか見てください。両側Action
を折り返すと、矢印の方向が逆になります。それが「反変」の意味です。タイプが異なると、矢印は反対の方向に進みます。明らかに、矢印の方向を2 回反転させることは、矢印の方向を維持することと同じことです。
参考文献:
機能の設計中に書いたブログ記事。下から始めます。
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx
分散がコンパイラによってタイプセーフであると判断される方法に関する最近の質問:
C# の差異規則