c# - 通过反射获取 IDictionary 项值设置器
问题描述
我正在尝试获取字典项值的 setter 函数。我知道对象是 Dictionary<TKey,TValue>,但我不知道 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 实际上是一个 Dictionary< TKey, TValue> 下面,也许我可以做一些反射魔法来获得设置器?
解决方案
当您枚举 a 的条目时Dictionary
,Key
并从的内部结构 ( )Value
复制到or的新实例。因此尝试修改这些是徒劳的,因为这些更改不会传播回字典。要修改,您必须使用它的 indexer 或,或类似的方法。Dictionary
Entry[]
KeyValuePair
DictionaryEntry
DictionaryEntry
Dictionary
Add
Remove
C# 索引器只是使用Dictionary<TKey,TValue>.Item属性的语法糖。所以在使用反射时,你必须改用这个属性。
要为 a 中的每个项目创建值设置器,您需要为每个项目获取一个键,然后在使用它的属性设置新值时将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);
}
推荐阅读
- sapui5 - 具有托管应用程序路由器的 SAP Cloud Foundry 多租户
- python - Unicode 字符串格式,因此文本会自动拆分为 PowerPoint 表格中的列
- angular - 在另一个组件中提交表单后更新数组
- ios - 'FIRMessaging' 没有可见的@interface 声明选择器'sendMessage:to:withMessageID:timeToLive:'
- spring-boot - Spring Boot 应用程序中的 com.codahale.metrics.Gauges
- python - 如何在 PySimpleGui 中设置两个可互换的可见容器?
- graph - Torch Tensorboard 图不反映代码
- terraform - Terraform - 从模块创建输出,但 splat 运算符不起作用
- holoviews - Datashader:GeoDataFrames 的分类颜色映射
- c++ - 无法为 .c 文件创建静态库。获取 src/crc16.c:35:10: 致命错误: defs.h: No such file or directory 错误