Как правильно составить словарьв словарь - PullRequest
3 голосов
/ 01 декабря 2011

У меня есть следующие классы:

public class BagA : Dictionary<string, BagB>
{}
public class BagB : Dictionary<string, object>
{}

Теперь с помощью отражения я создаю объект типа BagB, который я пытаюсь добавить к созданному мною объекту типа BagA:

object MyBagA // Created through reflection
object MyBagB // Created through reflection

((Dictionary<string,object>)MyBagA).Add("123",MyBagB);  //This doesnt work

Дает мне следующую ошибку: Невозможно привести объект типа 'BagA' к типу 'System.Collections.Generic.Dictionary`2 [System.String, System.Object]'.

Почему я не могу разыграть Dictionary<string, BagB> до Dictionary<string, object>?Какой лучший способ добавить мой предмет на основе этого сценария?возможно, анонимные методы ..?

Обратите внимание, что я бы предпочел не изменять мои классы BagA и BagB ...

Спасибо!

Ответы [ 4 ]

4 голосов
/ 01 декабря 2011

Здесь невозможно выполнить приведение типа, потому что Dictionary<string, BagB> и Dictionary<string, object> - это разные несовместимые типы. Вместо приведения Dictionary почему бы не привести значения вместо этого?

MyBagA.Add("123", (BagB)MyBagB);

Если было бы законно разыграть Dictionary<TKey, TValue>, тогда могли бы произойти очень злые вещи. Рассмотрим

Dictionary<string, BagB> map1 = ...;
Dictionary<string, object> map2 = SomeEvilCast(map1);
map2["foo"] = new object();

Что теперь будет, если я попытаюсь получить доступ к map1["foo"]? Тип значения - object, но статически оно равно BagB.

2 голосов
/ 01 декабря 2011

Поскольку вы использовали отражение для создания своих объектов, будет справедливо, что вам нужно продолжать использовать его для вызова их методов:

var addMethod = typeof(BagA).GetMethod("Add", new[] {typeof(string), typeof(BagB)});
addMethod.Invoke(MyBagA, new object[] {"123", MyBagB});
1 голос
/ 01 декабря 2011

Вы не можете напрямую приводить или преобразовывать экземпляр универсального типа в экземпляр типа с другими универсальными параметрами, ЕСЛИ ОБЩИЙ универсальный тип специально не определен как ковариантный (может рассматриваться как универсальный для базового класса фактического типа). объявленный универсальный тип) для этого конкретного параметра универсального типа, И вы пытаетесь привести тип к универсальному из базового класса его фактического типа. Например, IEnumerable<string> может рассматриваться как IEnumerable<object>, потому что строка происходит от объекта. Он не может рассматриваться как IEnumerable<char>, даже если все строки имеют только один символ, потому что String не является производным от Char.

Ковариация определяется в C # 4.0 с помощью ключевого слова out для универсального параметра, но, насколько мне известно, в отличие от IEnumerable, универсальный интерфейс IDictionary не указан как ковариантный. Кроме того, несмотря на то, что словарь является IEnumerable, он представляет собой IEnumerable из общих пар ключ / значение, и универсальные KVP не являются ковариантными, поэтому нельзя рассматривать универсальные параметры KVP как базовые типы.

Что вы можете сделать, это создать новый словарь нового типа и перенести все значения из старого. Если эти значения являются ссылочными типами, то при изменении подзначения одного из значений в словаре с типом ссылки он будет изменен в соответствующем значении другого словаря (если только вы не измените саму ссылку, назначив для этого нового экземпляра MyClass значение. ключ).

Маленький Линк делает это довольно легко:

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.
}
1 голос
/ 01 декабря 2011

Это принципиально невозможно.

Если бы вы смогли это сделать, вы бы могли добавить произвольные другие типы.

Вам нужно вызвать метод, используя отражение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...