2009-05-12 7 views
7

J'ai une instance qui implémente IDictionary<T, K>, je ne connais pas T et K à compiletime, et je veux en extraire tous les éléments. Je ne veux pas utiliser IEnumerable pour une raison quelconque, qui serait la seule interface non générique implémentée par IDictionary.Obtention des valeurs d'un IDictionary générique en utilisant la réflexion

code Je jusqu'à présent:

// 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]; 
} 

Je pourrais aussi appeler TryGetValue, mais je pense qu'il devrait être possible d'appeler l'opérateur []. Quelqu'un peut-il m'aider?

+0

Je ne suis pas sûr de comprendre la question. Voulez-vous utiliser l'opérateur [] au lieu d'obtenir la valeur de la propriété Item via Reflection? – Andy

+0

J'ai essayé cela avec un dictionnaire et la tentative d'utiliser l'indexeur/get_Item fonctionne pour moi. – Gishu

+0

@Andy: L'opérateur [] appelle réellement une propriété Item à l'exécution, qui n'est pas visible au moment de la compilation. @Gishu: comment avez-vous appelé l'indexeur? N'y a-t-il pas de propriété 'Item', seulement une méthode 'get_Item'? –

Répondre

21

Il serait préférable de figure sur le TKey/TValue et passer en code régulier via MakeGenericMethod - comme ceci:

(modifier - vous pouvez passer dans le otherInstance comme argument aussi, si ils sont du même type)

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); 
     } 
    }  
} 
+0

Ahh, je n'ai jamais vu cet initialiseur hashtable avant! +1 – leppie

+0

@Leppie - cela fait partie de la syntaxe d'initialisation de la collection; vous pouvez utiliser n'importe quelle méthode Add, mais si elle prend plusieurs arguments, vous envelopper avec un ensemble supplémentaire d'accolades. Donc {123, "abc"} appelle .Add (123, "abc") –

+0

Très sympa. Il y a un problème: iType.GetGenericArguments() peut échouer, car le type concret lui-même n'a pas besoin d'avoir les mêmes arguments génériques que l'IDictionary qu'il implémente. Mais j'ai le code pour obtenir les bons types déjà dans la question. –

4

Juste pour la fin, même si la solution de Marc Gravell est beaucoup plus agréable, c'est la manière dont cela fonctionne comme je l'ai déjà commencé:

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

Ceci appelle l'opérateur [] du dictionnaire.