Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Generic" Type arguments / Inheritance in type arguments

Tags:

c#

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?

like image 750
Janis F Avatar asked Jan 31 '26 07:01

Janis F


2 Answers

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.

like image 191
Jeppe Stig Nielsen Avatar answered Feb 01 '26 21:02

Jeppe Stig Nielsen


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.

like image 26
Servy Avatar answered Feb 01 '26 23:02

Servy