Suppose I have a function
DoSomething(Dictionary<object, object> dict)
why can't it be called with
DoSomething(new Dictionary<int, string>())?
Since intand string both inherit from object, I'd expect this to work. I know I could extend this to
DoSomething<T1, T2>(Dictionary<T1, T2> dict)
, but why won't the first option work?
It is because a Dictionary<int, string> is not a Dictionary<object, object>.
For that to be true, the class type Dictionary<TKey, TValue> would need to be covariant in both TKey and TValue, and covariance would need to work with value types (boxing).
First of all, in .NET, generic class types are always invariant. Only interface types and delegate types can be co- or contravariant.
Secondly, a Dictionary<,> is not semantically covariant. For example you could say:
myDict.Add(new Elephant(), new BankCustomer());
and that would not be very pleasant if myDict was actually a (run-time) Dictionary<int, string> in a Dictionary<object, object> variable.
Now, since .NET 4.5, some of the "non-dictionary" types implement covariant interfaces like IReadOnlyList<out T>. You might hope that the similar interface IReadOnlyDictionary<TKey, TValue> was covariant too. But it is not (the reason being given by the first comment by Servy below). So there is no hope for you.
Because Dictionary is invariant with respect to the types of both generic argument.
In C# all generic arguments are invariant. Generic arguments can only be covariant or contravariant for interfaces.
The IDictionary interface also could not be covariant or contravariant with respect to either type argument. Keys are passed out through the Keys property, in addition to being passed in...all over, and values are passed in using Add in addition to being passed out...all over. Because both types are used as both input and output, the types must be invariant.
That, and covariance/contravariance will not work with value types; it can only work with reference types.
Just to provide a simple example, if you were allowed to make such a cast then I would be able to call dict.Add("not an int", new Foo()) on the Dictionary<object, object>. I've now added a key that's not an int and a value that's not a string. Because allowing the cast would allow the use of types that don't match the signatures, it doesn't work.
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