Получение значений универсального IDictionary с использованием отражения

У меня есть экземпляр, который реализует 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, но я думаю, что должна быть возможность вызвать оператор []. Кто-нибудь может мне помочь?


person Stefan Steinegger    schedule 12.05.2009    source источник
comment
Я не уверен, что понимаю вопрос. Вы хотите использовать оператор [] вместо получения значения свойства Item через Reflection?   -  person Andy    schedule 12.05.2009
comment
Пробовал это со словарем‹Int16, String›, и попытка использовать indexer/get_Item работает для меня.   -  person Gishu    schedule 12.05.2009
comment
@Andy: оператор [] фактически вызывает свойство Item во время выполнения, которое не отображается во время компиляции. @Gishu: как ты назвал индексатор? Нет свойства «Item», есть только метод «get_Item»?   -  person Stefan Steinegger    schedule 12.05.2009


Ответы (2)


Было бы лучше разобраться с TKey / TValue и переключиться на обычный код через MakeGenericMethod - вот так:

(редактировать — вы также можете передать 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);
        }
    }    
}
person Marc Gravell    schedule 12.05.2009
comment
Ааа, я никогда раньше не видел этот инициализатор хеш-таблицы! +1 - person leppie; 12.05.2009
comment
@Leppie - это часть синтаксиса инициализатора коллекции; вы можете использовать любой метод Add, но если он принимает несколько аргументов, вы заключаете его в дополнительный набор фигурных скобок. Итак, {123,abc} вызывает .Add(123,abc) - person Marc Gravell; 12.05.2009
comment
Очень хорошо. Есть одна проблема: iType.GetGenericArguments() может завершиться ошибкой, потому что сам конкретный тип не обязательно должен иметь такие же универсальные аргументы, как реализуемый им IDictionary. Но у меня есть код, чтобы получить правильные типы уже в вопросе. - person Stefan Steinegger; 12.05.2009
comment
Я не уверен, что это возможно... обратите внимание, что я вызываю это для типа generic interface, а не для конкретного типа, и передаю объект as для этого универсального интерфейса . Можете ли вы показать/описать сценарий, в котором это не удастся? - person Marc Gravell; 12.05.2009
comment
@Marc: Ты прав, извини, я, конечно, плохо это прочитал. Он также должен работать с GetGenericTypeDefinition и открытым дженериком. Очень хорошая идея с общим методом. Я думаю, что могу использовать это во многих местах, чтобы снова переключиться с типов времени выполнения на дженерики. - person Stefan Steinegger; 12.05.2009
comment
Это работает на платформе, которая не поддерживает JIT-компиляцию? - person Dave Van den Eynde; 26.08.2010
comment
@ Дэйв, ты должен попробовать. Ограничения легких платформ иногда трудно предсказать. - person Marc Gravell; 27.08.2010

Просто для завершения, даже если решение Марка Гравелла намного лучше, вот как оно работает, как я уже начал:

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

Это вызывает оператор [] словаря.

person Stefan Steinegger    schedule 13.05.2009