views:

467

answers:

2

I have:

IDictionary<string, IDictionary<string, IList<long>>> OldDic1;

(just for illustration purposes, it is instantiated and has values - somewhere else)

Why can I do this: ?

Dictionary<string, IDictionary<string, IList<long>>> dic1 = 
  OldDic1 as Dictionary<string, IDictionary<string, IList<long>>>;

Basically dic1 after executing this line has all the values from OldDic1; works.

However when I do this:

Dictionary<string, Dictionary<string, List<long>>> dic1 = 
  OldDic1 as Dictionary<string, Dictionary<string, List<long>>>;

I get null, it is the same as casting except it doesn't crash and instead it returns null. So the question is why I can't cast it from the interfaces to types? is there solution, other then changing how it is stored in the first place?

A: 

The behavior is related to the as keyword in C#. A Dictionary is not the same thing as an IDictionary.

If you were casting the other way, you may be able to get it to work in the next version of .NET which has increased support for covariance and contravariance.

The solution you might want to determine why you need to cast to the concrete Dictionary/List, and if it's required, then change the storage of the type.

micahtan
the reason I have to cast to concrete type is because of DataContractJsonSerializer, which requires type. when the object is of conrete type I can say dic1.GetType(), however if I do this on the interfaces, the DataContractJsonSeralizer .writeobject throws exception
ra170
+2  A: 

You can only re-cast the outermost interface/class name, not the generic parameters. The reason your second cast doesn't work is the same reason you can't cast from one array type to another even if you can cast the "contained" objects. Here's why:

List<object> objects;
List<string> strings;

objects = strings as List<object>;
// Uh oh, that's not a string!
objects.Add(42); 

objects = new List<object> { "The", "answer", "is", 42 };
// Uh oh, now strings contains an integer?
strings = objects as List<string>;

The same thing would happen in your second case. You could add some kind of IDictionary to OldDic1 which is not actually a Dictionary, and then dic1 would blow up. It would have a non-Dictionary value. Ruh roh!

So, when you have containers you can change from IList<X> to List<X> and back as long as X is identical for each.

John Kugelman
Except that you CAN cast one array type to another in C# if the array element types are reference types that can be cast. Array conversions are covariant in C# if the element types are ref types. This kind of covariance is not actually type safe! It is unfortunate that this was added to the type system.
Eric Lippert