Reputation: 2874
I have the following classes:
public class BagA : Dictionary<string, BagB>
{}
public class BagB : Dictionary<string, object>
{}
Now, through reflection I'm creating an object of type BagB which I'm trying to add to an object I created of type BagA:
object MyBagA // Created through reflection
object MyBagB // Created through reflection
((Dictionary<string,object>)MyBagA).Add("123",MyBagB); //This doesnt work
Gives me the following error: Unable to cast object of type 'BagA' to type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]'.
Why can't I cast a Dictionary<string, BagB>
to Dictionary<string, object>
?
Which is the best way to add my Item based on this scenario? perhaps Anonymous methods..?
Notice that I would prefer not having to modify my classes BagA and BagB...
Thanks!
Upvotes: 4
Views: 5006
Reputation: 71591
You cannot directly cast or convert an instance of a generic type to an instance of the type with different generic parameters, UNLESS the generic type is specifically defined as covariant (able to be treated as a generic of a base class of the actual declared generic type) on that particular generic type parameter, AND you are attempting to cast the type to a generic of a base class of its actual type. For instance, an IEnumerable<string>
can be treated as an IEnumerable<object>
because string derives from object. It cannot be treated as an IEnumerable<char>
even if all the strings only have one character, because String does not derive from Char.
Covariance is definable in C# 4.0 using the out
keyword on the generic parameter, but to my knowledge, unlike IEnumerable, the generic IDictionary interface is not specified as covariant. In addition, even though a Dictionary is IEnumerable, it's an IEnumerable of the generic key/value pairs, and generic KVPs are not covariant, so you cannot treat the KVP's generic parameters as base types.
What you can do is create a new Dictionary of the new type and transfer all the values from the old one. If those values are reference types, changing a sub-value of one Dictionary's reference-typed Value will change it in the corresponding Value of the other Dictionary (unless you change the reference itself, by assigning a new instance of MyClass to the Value for that key).
A little Linq makes this one pretty easy:
Dictionary<string, MyClass> MyStronglyTypedDictionary =
new Dictionary<string, MyClass>();
//populate MyStronglyTypedDictionary
//a Dictionary<T1, T2> is an IEnumerable<KeyValuePair<T1, T2>>
//so most basic Linq methods will work
Dictionary<string, object> MyGeneralDictionary =
MyStronglyTypedDictionary.ToDictionary(kvp=>kvp.Key, kvp=>(object)(kvp.Value));
...
//Now, changing a MyClass instance's data values in one Dictionary will
//update the other Dictionary
((MyClass)MyGeneralDictionary["Key1"]).MyProperty = "Something else";
if(MyStronglyTypedDictionary["Key1"].MyProperty == "Something else")
{
//the above is true; this code will execute
}
//But changing a MyClass reference to a completely new instance will
//NOT change the original Dictionary
MyGeneralDictionary["Key1"] = new MyClass{MyProperty = "Something new"};
if(MyStronglyTypedDictionary["Key1"].MyProperty == "Something else")
{
//the above is STILL true even though the instance under this key in the
//other Dictionary has a different value for the property, because
//the other dictionary now points to a different instance of MyClass;
//the instance that this Dictionary refers to never changed.
}
Upvotes: 1
Reputation: 755587
There is no way to do a cast here because Dictionary<string, BagB>
and Dictionary<string, object>
are different incompatible types. Instead of casting the Dictionary
why not cast the values instead?
MyBagA.Add("123", (BagB)MyBagB);
If it were legal to cast the Dictionary<TKey, TValue>
then then very evil things could happen. Consider
Dictionary<string, BagB> map1 = ...;
Dictionary<string, object> map2 = SomeEvilCast(map1);
map2["foo"] = new object();
What would now happen if I tried to access map1["foo"]
? The type of the value is object
but it's statically typed to BagB
.
Upvotes: 4
Reputation: 727137
Since you used reflection to create your objects, it's only fair that you need to continue using it to call their methods:
var addMethod = typeof(BagA).GetMethod("Add", new[] {typeof(string), typeof(BagB)});
addMethod.Invoke(MyBagA, new object[] {"123", MyBagB});
Upvotes: 2
Reputation: 888293
This is fundamentally impossible.
Had you been able to do that, you would be able to add arbitrary other types.
You need to call the method using reflection.
Upvotes: 2