Casting a generic dictionary containing a generic dictionary
Solution 1
IDictionary
does not support covariance.
Look here IDictionary<TKey, TValue> in .NET 4 not covariant
Solution 2
The other answers are right, but just to be crystal clear as to why this is illegal, consider the following:
interface IAnimal {}
class Tiger : IAnimal {}
class Giraffe : IAnimal {}
...
Dictionary<string, Giraffe> d1 = whatever;
IDictionary<string, IAnimal> d2 = d1; // suppose this were legal
d2["blake"] = new Tiger(); // What stops this?
No mortal hand can stop you putting a tiger into a dictionary of IAnimals. But that dictionary is actually constrained to contain only giraffes.
For the same reason you can't go the other way either:
Dictionary<string, IAnimal> d3 = whatever;
d3["blake"] = new Tiger();
IDictionary<string, Giraffe> d4 = d3; // suppose this were legal
Giraffe g = d4["blake"]; // What stops this?
Now you're putting a tiger in a variable of type giraffe.
Generic interface covariance is only legal in C# 4 if the compiler can prove that situations like this cannot arise.
Solution 3
The most you will be able to do is
IDictionary<string, Dictionary<string, bool>> viaInterface = someConcreteInstance
The reason your inner dictionary cannot be referenced differently here (or via a cast) is that while Dictionary<string, bool>
is an IDictionary<string, bool>
, not all IDictionary
objects will be Dictionary
objects. As such, obtaining a pure interface cast would then seemingly allow you to add other <string, IDictionary<string, bool>>
pairs to the original collection, when clearly there could be type violations for the original object. Therefore, this is not supported.
Related videos on Youtube
nicodemus13
Updated on June 04, 2022Comments
-
nicodemus13 almost 2 years
I have:
var someConcreteInstance = new Dictionary<string, Dictionary<string, bool>>();
and I wish to cast it to an interface version, i.e.:
someInterfaceInstance = (IDictionary<string, IDictionary<string, bool>>)someConcreteInstance;
'someInterfaceInstance' is a public property:
IDictionary<string, IDictionary<string, bool>> someInterfaceInstance { get; set; }
This compiles correctly, but throws a runtime casting error.
Unable to cast object of type 'System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.Dictionary`2[System.String,System.Boolean]]' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Collections.Generic.IDictionary`2[System.String,System.Boolean]]'.
What am I missing? (Problems with the nested generic type/Property?)
-
nicodemus13 about 13 yearsOh, is that the problem! (even in .net 4?) Do you have any suggestions on how to get the result I want?
-
Aliostad about 13 yearsDepends what you are trying to do. Post another question on what you are trying to achieve and ask for the best design.
-
nicodemus13 about 13 yearsare you sure the last line is clear as you've written it? My original code compiles fine, but throws a runtime error, so the compiler seems to permit such situations.
-
Eric Lippert about 13 years@nicodemus13: Your code has a cast operator in it; mine does not. The cast operator means "take this conversion which would have been illegal at compile time and make it legal; if it turns out to be illegal at runtime, throw an exception". And hey, guess what? That's what's happening. You're telling the compiler "assume that this illegal conversion is going to work out just fine" and then it doesn't, and you get an exception. The purpose of a reference type cast operator is to move the burden of type checking from the compiler to the runtime. The burden doesn't vanish, it just moves.
-
Jeffrey L Whitledge about 13 years"Giraffe, giraffe, burning bright/In the forests of the night". Good thing the C# type system prevents such tragedies as this!