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