3

I'm wondering why I can't just cast (I have a vague idea this might have something to do with that co/contravariance stuff?), and am I forced to copy the elements of the first dictionary to a new one in order to get the type I want?

J Cooper
  • 16,891
  • 12
  • 65
  • 110
  • paste your code example.. you are probably not using Dictionary<> properly.. – MethodMan Jan 04 '12 at 21:27
  • 2
    This has been answered many times as it's a very frequent question. The short answer is because a reference to Dictionary> would allow you to add an T2[] array, which would be incorrect since the type is Dictionary> – James Michael Hare Jan 04 '12 at 21:29
  • @James Michael Hare I see. Sorry for the duplicate; I'm sure someone will close it. – J Cooper Jan 04 '12 at 21:33
  • 1
    This is rather trivial operation with the `ToDictionary` extension method. However, the result is a *new* Dictionary: `var d2 = d1.ToDictionary(kv => kv.Key, kv => kv.Value.AsEnumerable())` or so. –  Jan 04 '12 at 21:36
  • @JCooper: No worries, I haven't found the duplicate yet - it may have been a subtle variation - but I tried to sum it up below, though not in the great detail Eric Lippert always provides in his answers... – James Michael Hare Jan 04 '12 at 21:40

3 Answers3

10

You cannot do this because they aren't the same type. Consider:

        var x = new Dictionary<string, List<int>>();

        // won't compile, but assume it could...
        Dictionary<string, IEnumerable<int>> xPrime = x;

        // uh oh, would allow us to legally add array of int!
        xPrime["Hi"] = new int[13];

Does this make sense? Because Dictionary<string, List<int>> says the TValue is List<int> which means you can Add() a List<int> as a value. If you could cast this to a Dictionary<string, IEnumerable<int>> it would mean the value type is IEnumerable<int> which would mean you could Add() any IEnumerable<int> (int[], HashSet<int>, etc) which would violate the original type.

So, a List<T> can be converted to a IEnumerable<T> reference because List<T> implements IEnumerable<T>, but that does not mean that Dictionary<TKey, List<TValue>> implements/extends Dictionary<TKey, IEnumerable<TValue>>.

Put more simply:

Dictionary<int, Dog> 

Can't convert to

Dictionary<int, Animal>

Because the latter would allow you to add Cat, Squirrel, etc.

James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • Makes sense; I'll make a copy then (I won't actually be modifying the resulting collection, but the type system doesn't know that). – J Cooper Jan 04 '12 at 21:44
  • JCooper: Cool, as pst mentioned above, you can use LINQ to do that for you, like: `Dictionary> xPrime = x.ToDictionary(pair => pair.Key, pair => pair.Value.AsEnumerable());` – James Michael Hare Jan 04 '12 at 21:47
1

You can not cast, because it still is a Dictionary<T1, List<T2>>

Lets say

Dictionary<string, List<int>> d1 = new Dictionary<string, List<int>>();
Dictionary<string, IEnumerable<int>> d2 = (Dictionary<string, IEnumerable<int>>)d1; // this is the invalid cast
d2["one"] = new int[0]; // valid for d2
List<int> list1 = d1["one"]; // would fail
Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
-1

yes, it's an issue with covariance. I believe this was corrected with .net 4.0

Jason Meckley
  • 7,589
  • 1
  • 24
  • 45