2014-02-19 7 views
5

Wie programmiere ich programmgesteuert, ob ein Objekt in einem Schlüsselwertpaar aufzählbar ist?KeyValuePair <string, object> Ist das Objekt aufzählbar?

Ich muss wissen, ob ein Objekt im Wertfeld eine Liste oder ein Array ist. Ich sollte in der Lage sein, zu bestimmen, welche Art von enumerable Art das Objekt (zB eine Liste von Strings oder ein Array von ints)

List<KeyValuePair<string, object>> lKVP = new List<KeyValuePair<string, object>>(); 
List<string> lS = new List<string> { "s1", "s2" }; 

lKVP.Add(new KeyValuePair<string, object>("PassPhrase", "E92D8719-38A6-0000-961F-0E66FCB0A363")); 
lKVP.Add(new KeyValuePair<string, object>("Test", lS)); 

Was habe ich versucht:

1)

foreach(KeyValuePair<string,object> kvp in _ParameterReplacement) 
{ 
    if(kvp.Value is Enumerable) 
    { 
     Console.WriteLine("Yes"); 
    } 
    else 
    { 
     Console.WriteLine("No"); 
    } 
} 

2)

foreach(KeyValuePair<string,object> kvp in _ParameterReplacement) 
{ 
    if(kvp.Value.GetType() == typeof(IEnumerable<object>)) 
    { 
     Console.WriteLine("Yes"); 
    } 
    else 
    { 
     Console.WriteLine("No"); 
    } 
} 

Antwort

2

Mit dynamic

Sie könnten das dynamic Stichwort nutzen, dies zu tun, aber ich denke, es könnte für Sie zu langsam sein.

Dies ist jedoch, wie Sie es tun könnten. Dies ruft eine stark typisierte enumerate()-Methode für List<T> und T[] auf, oder wenn der Wert weder eine List noch ein Array ist, ruft er die Überladung von enumerate() auf, die nur ein Objekt aufnimmt.

Ich bin nicht ganz sicher, dass dies das ist, wonach Sie suchen, aber es gibt Ihnen eine stark typisierte Enumeration für die Listen und Arrays in Ihrer KVP-Liste.

Beachten Sie, dass gemäß Ihrer Spezifikation, berücksichtigt dies nur List und Array-Typen; andere zählbare Typen (wie Saiten, HashSet usw.) werden nicht berücksichtigt:

using System; 
using System.Collections.Generic; 

namespace ConsoleApp1 
{ 
    sealed class Program 
    { 
     void test() 
     { 
      List<KeyValuePair<string, object>> lKVP = new List<KeyValuePair<string, object>>(); 
      List<string> lS = new List<string> { "s1", "s2" }; 
      string[] aS = {"a1", "a2"}; 

      lKVP.Add(new KeyValuePair<string, object>("String", "E92D8719-38A6-0000-961F-0E66FCB0A363")); 
      lKVP.Add(new KeyValuePair<string, object>("Test", lS)); 
      lKVP.Add(new KeyValuePair<string, object>("IntNotEnumerable", 12345)); 
      lKVP.Add(new KeyValuePair<string, object>("Array", aS)); 

      foreach (KeyValuePair<string,object> kvp in lKVP) 
      { 
       enumerate((dynamic) kvp.Value); 
      } 
     } 

     static void enumerate<T>(List<T> list) 
     { 
      Console.WriteLine("Enumerating list of " + typeof(T).FullName); 

      foreach (var item in list) 
       Console.WriteLine(item); 

      Console.WriteLine(); 
     } 

     static void enumerate<T>(T[] array) 
     { 
      Console.WriteLine("Enumerating array of " + typeof(T).FullName); 

      foreach (var item in array) 
       Console.WriteLine(item); 

      Console.WriteLine(); 
     } 

     static void enumerate(object obj) 
     { 
      Console.WriteLine("Not enumerating type " + obj.GetType().FullName + " with value " + obj); 
      Console.WriteLine(); 
     } 

     static void Main(string[] args) 
     { 
      new Program().test(); 
     } 
    } 
} 

explizite Reflexion Mit

Hier ist ein Weg, es zu tun Reflexion verwenden, die dynamic vermeidet die Verwendung, die bedeutet, dass es viel schneller ist - aber wie Sie sehen können, ist es deutlich findiger!

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Reflection; 

namespace ConsoleApp1 
{ 
    sealed class Program 
    { 
     void test() 
     { 
      List<KeyValuePair<string, object>> lKVP = new List<KeyValuePair<string, object>>(); 
      List<string> lS = new List<string> { "s1", "s2" }; 
      string[] aS = {"a1", "a2"}; 

      lKVP.Add(new KeyValuePair<string, object>("String", "E92D8719-38A6-0000-961F-0E66FCB0A363")); 
      lKVP.Add(new KeyValuePair<string, object>("Test", lS)); 
      lKVP.Add(new KeyValuePair<string, object>("IntNotEnumerable", 12345)); 
      lKVP.Add(new KeyValuePair<string, object>("Array", aS)); 

      var listEnumerator = this.GetType().GetMethod("enumerateList", BindingFlags.NonPublic | BindingFlags.Static); 
      var arrayEnumerator = this.GetType().GetMethod("enumerateArray", BindingFlags.NonPublic | BindingFlags.Static); 

      foreach (KeyValuePair<string, object> kvp in lKVP) 
      { 
       MethodInfo genericEnumerator = null; 
       var arrayElemType = arrayElementType(kvp.Value); 

       if (arrayElemType != null) 
       { 
        genericEnumerator = arrayEnumerator.MakeGenericMethod(arrayElemType); 
       } 
       else 
       { 
        var listElemType = listElementType(kvp.Value); 

        if (listElemType != null) 
         genericEnumerator = listEnumerator.MakeGenericMethod(listElemType); 
       } 

       if (genericEnumerator != null) 
        genericEnumerator.Invoke(null, new[] { kvp.Value }); 
       else 
        Console.WriteLine("Not enumerating type: " + kvp.Value.GetType().FullName + "\n"); 
      } 
     } 

     static Type arrayElementType(object sequence) 
     { 
      if (sequence is IEnumerable) 
      { 
       var type = sequence.GetType(); 

       if (type.IsArray) 
        return type.GetElementType(); 
      } 

      return null; 
     } 

     static Type listElementType(object sequence) 
     { 
      if (sequence is IEnumerable) 
      { 
       var type = sequence.GetType(); 

       if (typeof(IList).IsAssignableFrom(type) && type.IsGenericType) 
        return type.GetProperty("Item").PropertyType; 
      } 

      return null; 
     } 

     static void enumerateList<T>(List<T> list) 
     { 
      Console.WriteLine("Enumerating list of " + typeof(T).FullName); 

      foreach (var item in list) 
       Console.WriteLine(item); 

      Console.WriteLine(); 
     } 

     static void enumerateArray<T>(T[] array) 
     { 
      Console.WriteLine("Enumerating array of " + typeof(T).FullName); 

      foreach (var item in array) 
       Console.WriteLine(item); 

      Console.WriteLine(); 
     } 

     static void Main(string[] args) 
     { 
      new Program().test(); 
     } 
    } 
} 
+0

Wenn Sie Ihren Reflexionscode verwenden, erhalte ich einen Objektverweis, der nicht an eine Instanz eines Objekts in Zeile gesendet wird genericEnumerator = listEnumerator.MakeGenericMethod (listElemType); – BossRoss

+0

@BossRoss Sie erhalten diesen Fehler nicht, wenn Sie meinen Beispielcode ohne Änderungen ausführen. Wenn Sie diesen Fehler erhalten, nachdem Sie den Code geändert haben, liegt das daran, dass es in der Klasse keine statische private Methode namens "enumerateList" gibt und daher "GetMethod()" null zurückgibt. Sie müssen diesen Aufruf ändern, um den entsprechenden Methodennamen und die Bindungsflags für die Methode anzugeben, die Sie aufrufen möchten. –

+0

Ich habe den Reflexionscode verwendet, es funktioniert gut (wenn die richtigen Methoden statisch sind) Danke für die Hilfe und die gute Antwort – BossRoss

1

Sie haben die implementierten Schnittstellen von kvp.Value zu überprüfen. Dies ist nur durch Reflexion möglich.

var type = kvp.Value.GetType(); 
if (type.IsArray) return type.GetElementType(); 
foreach (var i in type.GetInterfaces()) 
{ 
    if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)) 
    { 
     return i.GetGenericTypeArguments()[0]; 
    } 
} 
// not a generic collection 
return typeof(object); 

Auf diese Weise können Sie den Elementtyp der Sammlung zur Laufzeit bestimmen. Um die Elemente abzurufen, empfiehlt es sich jedoch, die nicht generische Schnittstelle IEnumerable zu verwenden, da Sie den (teuren) Reflektionsaufwand nicht benötigen. Die Überprüfung auf IEnumerable ist auch ein guter Ausgangspunkt, daher macht es keinen Sinn, den Elementtyp zu überprüfen, wenn kvp.Value keine Sammlung ist.

+0

"GetGenericTypeArguments" ist nicht zugänglich, Sie sollten 'GetGenericArguments' verwenden. Übrigens, deins funktioniert gut. – Teejay

1

Dies funktioniert für die meisten Enumerable-Typen:

Type objListType = null; 

if (kvp.Value is IEnumerable) { 

    if (kvp.Value.GetType().IsArray) 
     objListType = kvp.Value.GetType().GetElementType(); 
    else 
     objListType = kvp.Value.GetType().GetProperty("Item").PropertyType; 

} 
+1

Für Sätze (wie zum Beispiel 'HashSet ') funktioniert das nicht. – Georg

+0

@Georg Ja, ich schrieb ** Most ** – Teejay

+0

@ANeves Ich benutze es für Listen – Teejay

Verwandte Themen