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 typeaction<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:
Comments
Post a Comment