使用反射获取泛型IDictionary的值

11

我有一个实例实现了IDictionary<T, K>,但我在编译时不知道T和K,并且想要获取它的所有元素。出于某些原因,我不想使用IEnumerable,这是IDictionary实现的唯一非泛型接口。

到目前为止我的代码:

// getting types
Type iDictType = instance.GetType().GetInterface("IDictionary`2");
Type keyType = iDictType.GetGenericArguments()[0];
Type valueType = iDictType.GetGenericArguments()[1];

// getting the keys
IEnumerable keys = (IEnumerable)dictType.GetProperty("Keys")
  .GetValue(instance, null);

foreach (object key in keys)
{
  // ==> this does not work: calling the [] operator
  object value = dictType.GetProperty("Item")
    .GetValue(instance, new object[] {key } );


  // getting the value from another instance with TryGet
  MethodInfo tryGetValue = iDictType.GetMethod("TryGetValue");
  object[] arguments = new object[] { key, null };
  bool hasElement = (bool)tryGetValue.Invoke(otherInstance, arguments);
  object anotherValue = arguments[1];
}

我也可以调用TryGetValue方法,但是我认为应该可以调用[]操作符。有人能帮助我吗?


我不确定我理解这个问题。您是否想使用[]运算符来代替通过反射获取Item属性的值? - Andy
我尝试使用一个Dictionary<Int16,String>并尝试使用索引器/get_Item,这对我有效。 - Gishu
@Andy:[]运算符实际上在运行时调用了一个Item属性,这个属性在编译时是不可见的。 @Gishu:你是怎么调用索引器的?难道没有名为“Item”的属性,只有一个名为“get_Item”的方法吗? - Stefan Steinegger
2个回答

23

通过使用MakeGenericMethod,最好能够找出TKey/TValue,并切换到常规代码中 - 就像这样:

编辑 - 如果它们是相同类型的,则还可以将otherInstance作为参数传递)

static class Program
{
    static void Main()
    {
        object obj = new Dictionary<int, string> {
            { 123, "abc" }, { 456, "def" } };

        foreach (Type iType in obj.GetType().GetInterfaces())
        {
            if (iType.IsGenericType && iType.GetGenericTypeDefinition()
                == typeof(IDictionary<,>))
            {
                typeof(Program).GetMethod("ShowContents")
                    .MakeGenericMethod(iType.GetGenericArguments())
                    .Invoke(null, new object[] { obj });
                break;
            }
        }
    }
    public static void ShowContents<TKey, TValue>(
        IDictionary<TKey, TValue> data)
    {
        foreach (var pair in data)
        {
            Console.WriteLine(pair.Key + " = " + pair.Value);
        }
    }    
}

非常好。有一个问题:iType.GetGenericArguments()可能会失败,因为具体类型本身不需要具有与其实现的IDictionary相同的泛型参数。但是我已经在问题中有获取正确类型的代码了。 - Stefan Steinegger
我不确定它可以......注意,我在通用接口类型上调用它,而不是具体类型,并将对象作为该通用接口传递。您能展示/描述一个可能失败的场景吗? - Marc Gravell
@Marc:你说得对,很抱歉,我确实没有仔细读。使用GetGenericTypeDefinition和开放泛型也应该可以工作。通用方法的想法非常好。我认为我可以在许多地方使用它,从运行时类型切换回泛型。 - Stefan Steinegger
这个能在不支持JIT编译的平台上运行吗? - Dave Van den Eynde
@Dave - 你得试一试。轻量级平台的限制有时很难预测。 - Marc Gravell
显示剩余2条评论

6

仅为完整起见,即使Marc Gravell的解决方案更好,这也是我已经开始的工作方式:

object value = dictType.GetMethod("get_Item")
  .Invoke(instance, new object[] { key });

这将调用字典的[]运算符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接