17

これは、私が頭を抱えているのに苦労しているものです。Action<T>それは反変であり、おそらくそのように宣言されていることを理解しています。

internal delegate void Action<in T>(T t);

ただし、 anが共変である理由がわかりませんAction<Action<T>>Tまだ出力位置にありません。誰かがこの背後にある理由/ロジックを説明しようとしてくれれば、本当に感謝しています.

少し掘り下げて、それを説明しようとするこのブログ投稿を見つけました。特に、「入力の共分散の説明」サブセクションの下でここで意味されていることを完全には理解していませんでした。

「Derived -> Base」のペアを「Action -> Action」のペアに置き換えても同じです。

4

4 に答える 4

25

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ください。aaaMeta

さて、何に渡すことができaaaますか?任意Action<Animal>:

aaa( (Animal animal) => { animal.Feed(); } );

そして、どうなりますか?Meta新しい魚でデリゲートを呼び出す にデリゲートを渡し、魚に餌を与えます。問題ない。

T はまだ出力位置にありません。誰かがこの背後にある理由/ロジックを説明しようとしてくれれば、本当に感謝しています.

「入力/出力」位置はニーモニックです。共変型は出力位置に T を持つ傾向があり、反変型は入力位置に T を持つ傾向がありますが、それは普遍的に当てはまるわけではありません。ほとんどの場合、これが正しいため、キーワードとしてinandを選択しました。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# の差異規則

于 2013-05-09T16:31:59.843 に答える
1

object をルートとする継承ツリーがあります。ツリー上のパスは通常、次のようになります。

object -> Base -> Child

ツリーの上位の型のオブジェクトは、ツリーの下位の型の変数に常に割り当てることができます。ジェネリック型の共分散とは、実現された型がツリーに従って関連付けられていることを意味します。

object -> IEnumerable<object> -> IEnumerable<Base> -> IEnumerable<Child>

object -> IEnumerable<object> -> IEnumerable<IEnumerable<object> -> ...

反変性とは、実現された型がツリーを逆にする方法で関連付けられることを意味します。

object -> Action<Child> -> Action<Base> -> Action<object>

レベルをさらに深くすると、ツリーを再び逆にする必要があります

object -> Action<Action<object>> -> Action<Action<Base>> -> Action<Action<Child>> -> Action<object>

反変性のある ps では、オブジェクト階層はもはやツリーではなく、実際には有向非巡回グラフです。

于 2013-05-09T17:31:01.753 に答える