Я пытаюсь использовать System.Reflection для изменения значений в общем словаре, в котором мне неизвестны типы IKey и IValue. Я могу установить значения Dictionary.Entry.value для каждой записи в словаре, используя серию операторов GetField(), GetValue() и SetValue() (код ниже), но значения, возвращаемые dict[key ] по-прежнему возвращают неизмененные значения.
Вот простой пример того, что я хочу сделать (после этого будет реальный код):
dict["some key"] = "some value";
// Use reflection to do a SetValue dict.entries[0] to "some new value"
Printing out dict["some key"] should now display "some new value"
Вместо этого он печатает исходное значение. В приведенном ниже коде вы увидите, что ключ совпадает, и вызывается GetValue() после того, как SetValue() получает новое значение. Я в недоумении, почему это происходит.
Вот код:
const BindingFlags BINDING_FLAGS_ALL = (BindingFlags) 65535;
Dictionary<string, string> dict = new Dictionary<string, string>();
dict["key1"] = "original value";
object entry = ((Array) dict.GetType().GetField("entries", BINDING_FLAGS_ALL).GetValue(dict)).GetValue(0);
object key = entry.GetType().GetField("key", BINDING_FLAGS_ALL).GetValue(entry);
FieldInfo val_field = entry.GetType().GetField("value", BINDING_FLAGS_ALL);
object val = val_field.GetValue(entry);
Console.WriteLine($"original value at '{key}' == '{val}'");
val_field.SetValue(entry, "changed value!");
val = val_field.GetValue(entry);
Console.WriteLine($"new value at '{key}' == '{val}'");
val = dict[(string) key];
Console.WriteLine($"dict[{key}] == '{val}'");
Результат:
original value at 'key1' == 'original value'
new value at 'key1' == 'changed value!'
dict[key1] == 'original value'
Заранее спасибо за руководство! дд
Dictionary<TKey,TValue>
реализует необобщенный IDictionary
— разве вы не можете просто использовать это?
Не делай этого. Поскольку он основан на частной реализации, он очень хрупкий и зависит от платформы. Он не работает даже в .NET [Core], где имена полей имеют префикс подчеркивания. И почему это значение BindingFlags
с необдуманной установкой младших битов, а не значимой константой? Почему бы вам просто не привести dict
к IDictionary
и не использовать неуниверсальный индексатор?
Обновлять
Как указано в комментариях, вы можете сначала преобразовать словарь в необобщенный IDictionary
:
var dict = new Dictionary<string, string>();
((IDictionary)dict)["key1"] = "changed value!";
// Works
((IDictionary)dict)[10] = 50;
// Also works, just generates
// an exception because the dictionary is of type string
// (but at least compiles and runs)
Оригинальный ответ
Каждый тип, имеющий индексатор, также включает в себя скрытые методы: get_Item
и set_Item
.
Мой подход заключается в вызове метода set_Item
с отражением:
Dictionary<string, string> dict = new Dictionary<string, string>();
dict["key1"] = "original value";
var type = dict.GetType();
var setItemMethod = type.GetMethod("set_Item");
setItemMethod.Invoke(dict, new object[] { "key1", "changed value!" });
Console.WriteLine(dict["key1"]);
Эта программа отобразит результат changed value!
.
Это работает даже для словарей, тип которых вы не знаете, просто измените параметры метода Invoke
на любой тип:
setItemMethod.Invoke(dict, new object[] { 10, 50 }); // for integers
Это именно то, что мне нужно и очень просто. Спасибо!
((IDictionary)dict)[objKey] = objValue
было бы гораздо проще.
Entry
— это структура.
Итак, вы работаете с его копией, а не с той, которую использует Dictionary.
Очень хороший момент и очень полезно это знать. Спасибо!
Меня немного смущает конечная цель... как бы вы изменили значение записи, если даже не знаете, какого типа это значение? Как узнать, какие клавиши нужно изменить, если вы не знаете тип клавиш?