Использование отражения для установки значения записи в общем словаре

Я пытаюсь использовать 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'

Заранее спасибо за руководство! дд

Меня немного смущает конечная цель... как бы вы изменили значение записи, если даже не знаете, какого типа это значение? Как узнать, какие клавиши нужно изменить, если вы не знаете тип клавиш?

ipodtouch0218 27.06.2024 20:41
Dictionary<TKey,TValue> реализует необобщенный IDictionary — разве вы не можете просто использовать это?
Marc Gravell 27.06.2024 20:49

Не делай этого. Поскольку он основан на частной реализации, он очень хрупкий и зависит от платформы. Он не работает даже в .NET [Core], где имена полей имеют префикс подчеркивания. И почему это значение BindingFlags с необдуманной установкой младших битов, а не значимой константой? Почему бы вам просто не привести dict к IDictionary и не использовать неуниверсальный индексатор?

György Kőszeg 27.06.2024 21:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Обновлять

Как указано в комментариях, вы можете сначала преобразовать словарь в необобщенный 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

Демо

Это именно то, что мне нужно и очень просто. Спасибо!

devopsdinosaur 27.06.2024 21:13
((IDictionary)dict)[objKey] = objValue было бы гораздо проще.
György Kőszeg 27.06.2024 21:23

Entry — это структура.

Итак, вы работаете с его копией, а не с той, которую использует Dictionary.

Очень хороший момент и очень полезно это знать. Спасибо!

devopsdinosaur 27.06.2024 21:13

Другие вопросы по теме