I can't for the life of me figure this out. Say I have the following two dictionary objects:
// Assume "query" is a LINQ queryable.
Dictionary<string, int> d1 = query.ToDictionary(k => k.Key, v => v.Value);
Dictionary<string, int> d1 = query.ToDictionary(k => k.Key, v => v.Value);
The following statement produces a compile time error that an implicit conversion between a Dictionary and IDictionary is not possible:
// Compile time error!
Tuple<IDictionary<string, int>, IDictionary<string, int>> = Tuple.Create(d1, d2);
I have to do the conversion explicitly:
Tuple<IDictionary<string, int>, IDictionary<string, int>> = Tuple.Create(d1 as IDictionary<string, int>, d2 as IDictionary<string, int>);
I am not understanding why the compiler can't figure out the covariance operation - Dictionary implements IDictionary - especially since something like this will of course work as we all know:
IDictionary<string, int> d3 = d1;
I am sure there is a good reason for this behavior and I am curious what it is.
Update 1: Just to clarify, I am curious about the behavior not how to solve the problem. I am aware of the different solutions :)
Update 2:
Thank you all for the great answers. I did not know Tuple
was invariant and now I do.
Basically, the problem is that the Tuple
family isn't covariant in its type arguments. It can't be, because it's a class. An interface or delegate version could be created that would be covariant, however, as there are no members accepting the type parameters in input positions.
This is simplest to see with Tuple<T1>
:
Tuple<string> stringTuple = Tuple.Create("Foo");
Tuple<object> objectTuple = stringTuple;
This call:
Tuple.Create(d1, d2);
... is inferring both type arguments as Dictionary<string, int>
, so you're trying to convert from Tuple<Dictionary<string, int>, Dictionary<string, int>>
to
Tuple<IDictionary<string, int>, IDictionary<string, int>>
, which doesn't work.
The version with as
changes the argument types so that type inference gives the desired type arguments - but it would be simpler to just write the type arguments directly, and avoid inference entirely, as per Sebastian's answer:
Tuple.Create<IDictionary<string, int>, IDictionary<string, int>>(d1, d2)
If you use var
at this point, it's not so bad:
var x = Tuple.Create<IDictionary<string, int>, IDictionary<string, int>>(d1, d2);
The type of x
will now be Tuple<IDictionary<string, int>, IDictionary<string, int>>
which is what you want.
EDIT: As noted in comments, you might as well just use the constructor at that point:
var x = new Tuple<IDictionary<string, int>, IDictionary<string, int>>(d1, d2);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With