c# - Why is Action<Action<T>> covariant? -


this i'm having hard time wrapping head around. understand action<t> contravariant , declared such.

internal delegate void action<in t>(t t); 

however, don't understand why action<action<t>> covariant. t still not in output position. i'd appreciate if try explain reasoning / logic behind this.

i dug around little bit , found this blog post tries explain it. in particular, didn't quite follow meant here under "explanation covariance of input" subsection.

it same natural if “derived -> base” pair replaced “action -> action” pair.

ok, first of let's clear mean saying action<action<t>> covariant. mean following statement holds:

  • if object of reference type x may assigned variable of reference type y, object of type action<action<x>> may assigned variable of reference type action<action<y>>.

well, let's see if works. suppose have classes fish , animal obvious inheritance.

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); 

what do? pass delegate dosomething meta. creates new fish, , dosomething makes fish swim. no problem.

so far good. question is, why should legal?

action<action<animal>> aaa = aaf; 

well, let's see happens if allow it:

aaa(af); 

what happens? same thing before, obviously.

can make go wrong here? if pass other af aaa, remembering doing pass along meta.

well, can pass aaa? action<animal>:

aaa( (animal animal) => { animal.feed(); } ); 

and happens? pass delegate meta, invokes delegate new fish, , feed fish. no problem.

t still not in output position. i'd appreciate if try explain reasoning / logic behind this.

the "input/output" position thing mnemonic; covariant type tend have t in output position , contravariant type tend have t in input position, not universally true. majority of cases, that's true, why chose in , out keywords. matters types can used in typesafe manner.

here's way think it. covariance preserves direction of arrow. draw arrow string --> object, can draw "same" arrow ienumerable<string> --> ienumerable<object>. contravariance reverses direction of arrow. here arrow x --> y means reference x may stored in variable of type y:

fish                         -->     animal   action<fish>                 <--     action<animal>  action<action<fish>>         -->     action<action<animal>> action<action<action<fish>>> <--     action<action<action<animal>>> ... 

see how works? wrapping action around both sides reverses direction of arrow; that's "contravariant" means: types vary, arrows go in contra -- opposing -- direction. reversing direction of arrow twice same thing preserving direction of arrow.

further reading:

my blog articles wrote while designing feature. start bottom:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

a recent question how variance determined typesafe compiler:

variance rules in c#


Comments

Popular posts from this blog

linux - xterm copying to CLIPBOARD using copy-selection causes automatic updating of CLIPBOARD upon mouse selection -

c++ - qgraphicsview horizontal scrolling always has a vertical delta -