получение установщика значения элемента IDictionary через отражение - PullRequest
0 голосов
/ 16 ноября 2018

Я пытаюсь получить функцию установки значения элемента словаря.Я знаю, что объект - это Dictionary , но я не знаю типы Tkey и TValue, поэтому я думаю, что мой единственный способ - использовать IDictionary.

В псевдокоде я хочучтобы сделать что-то вроде этого;

Action<object> keySetter = dictionary.items[index].value.setter
Action<object> valueSetter = dictionary.items[index].key.setter

К сожалению, у IDictionary нет индексатора, и я не уверен, как добраться до фактических ключей и значений.

Прямо сейчас я перебираюзаписи словаря и получают установщик от этого, но всякий раз, когда я вызываю установщик, он, кажется, не меняется на значение в словаре.Поэтому я подозреваю, что DictionaryEntry является копией и не указывает на фактическое значение в словаре.

//for simplicity sake a Dictionary is added here, but usually the TKey and Tvalue are not known
IDictionary target = new Dictionary<int, string>();
target.Add( 0, "item 1" );

foreach ( DictionaryEntry dictionaryEntry in target )
{
    //want to get the setter for the item's key setter and value setter
    PropertyInfo keyProperty = dictionaryEntry.GetType().GetProperty( "Key" );
    PropertyInfo valueProperty = dictionaryEntry.GetType().GetProperty( "Value" );

    Action<object> keySetter = ( val ) =>
    {
        keyProperty.SetMethod.Invoke( dictionaryEntry, new object[] { val } );
    };

    Action<object> valueSetter = ( val ) =>
    {
        valueProperty.SetMethod.Invoke( dictionaryEntry, new object[] { val } );
    };

    keySetter.Invoke( 1 );
    valueSetter.Invoke( "item 1 value succesfully modified" );

    Console.WriteLine( target.Keys ); //no change
    Console.WriteLine( target.Values ); //no change
}

Поскольку я действительно знаю, что IDictionary на самом деле представляет собой словарь , возможно, яможет сделать магию отражения, чтобы получить сеттер таким образом?

1 Ответ

0 голосов
/ 16 ноября 2018

При перечислении записей Dictionary, Key и Value копируются из внутренних структур Dictionary (Entry[]) в новые экземпляры KeyValuePair или DictionaryEntry. Поэтому пытаться изменить эти DictionaryEntry бесполезно, потому что эти изменения не распространяются обратно в словарь. Чтобы изменить Dictionary, вы должны использовать его индексатор или Add, Remove или аналогичные методы.

Индексаторы

C # - это просто синтаксический сахар, использующий свойство Dictionary .Item . Поэтому, когда вы используете отражение, вы должны использовать это свойство.

Чтобы создать установщик значений для каждого элемента в Dictionary, вам необходимо получить ключ для каждого из этих элементов, а затем использовать его в качестве аргумента index при установке нового значения для Dictionary, используя его Item имущество. Создать установщик ключей сложнее, потому что Dictionary не поддерживает изменение существующего ключа. Что вам нужно сделать, так это удалить существующий элемент из Dictionary и вставить новый с новым ключом:

// Dictionary.Item property we will use to get/set values
var itemProp = target.GetType().GetProperty("Item");

// Since you want to modify dictionary while enumerating it's items (which is not allowed),
// you have to use .Cast<object>().ToList() to temporarily store all items in the list
foreach (var item in (target as IEnumerable).Cast<object>().ToList())
{
    // item is of type KeyValuePair<TKey,TValue> and has Key and Value properties.
    // Use reflection to read content from the Key property
    var itemKey = item.GetType().GetProperty("Key").GetValue(item);

    Action<Object> valueSetter = (val) => itemProp.SetValue(target, val, new object[] { itemKey });

    // valueSetter(someNewValue);

    // It's not possible to just change the key of some item. As a workaround,
    // you have to remove original item from the dictionary and insert a new item
    // with the original value and with a new key.
    Action<Object> keySetter = (key) =>
    {
        // read value from the dictionary for original key
        var val = itemProp.GetValue(target, new object[] { itemKey });

        // set this value to the disctionary with the new key
        itemProp.SetValue(target, val, new object[] { key });

        // remove original key from the Dictionary
        target.GetType().GetMethod("Remove").Invoke(target, new Object[] { itemKey });

        // need to "remember" the new key to allow the keySetter to be called repeatedly
        itemKey = key;
    };

    // keySetter(someNewKey);
}
...