devopsdinosaur
devopsdinosaur

Reputation: 21

Using reflection to set an entry value in generic dictionary

I am attempting to use System.Reflection to modify values in a generic dictionary in which I do not know the types of the IKey and IValue. I am able to set the values of the Dictionary.Entry.value for each entry in the dictionary using a series of GetField(), GetValue(), and SetValue() statements (code below), but the values returned by dict[key] still return the unaltered values.

Here is simple example of what I want to do (real code is after this):

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"

Instead, it prints out the original value. In the code below you'll see that the key is a match, and a GetValue() call after the SetValue() retrieves the new value. I'm at a loss as to why this is happening.

Here is the code:

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}'");

The output is:

original value at 'key1' == 'original value'
new value at 'key1' == 'changed value!'
dict[key1] == 'original value'

Thanks in advance for the guidance! dd

Upvotes: 0

Views: 110

Answers (2)

winscripter
winscripter

Reputation: 740

Update

As pointed out in the comments, you can cast the dictionary to a non-generic IDictionary first:

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)

Original answer

Each type that has an indexer also includes hidden methods: get_Item and set_Item.

My approach is to invoke the set_Item method with reflection:

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"]);

This program will display the output changed value!.

It works even for dictionaries that you don't know the type of, simply change parameters of the Invoke method to any type:

setItemMethod.Invoke(dict, new object[] { 10, 50 }); // for integers

Demo

Upvotes: 2

Ivan Petrov
Ivan Petrov

Reputation: 4855

Entry is a struct.

So you are working with a copy of it, not the one that Dictionary internally uses.

Upvotes: 1

Related Questions