2014-02-19 42 views
5

如何以編程方式確定Key值對中的對象是否可枚舉?KeyValuePair <string,object>對象是否可枚舉?

我需要知道值字段中的對象是List還是Array。我應該能夠確定什麼樣的枚舉類型的對象(如字符串列表或整型數組)

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)); 

我曾嘗試:

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"); 
    } 
} 

回答

2

使用dynamic

你可以充分利用dynamic關鍵字做到這一點,但我認爲這可能是你太慢了。

但是,這是你如何做到的。這將爲List<T>T[]調用強類型的enumerate()方法,或者如果該值既不是列表也不是數組,它將調用enumerate()的過載,該過載只需要一個對象。

我並不完全確定這是你之後的事情,但它確實爲您提供了KVP列表中列表和陣列的強類型枚舉。

請注意,根據您的規範,這隻考慮列表和數組類型;其他枚舉類型(如字符串,HashSet的等等)不被認爲是:

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(); 
     } 
    } 
} 

使用顯式反射

這裏的使用避免了使用dynamic反射做這件事的方式,這意味着它要快得多 - 但正如你所看到的那樣,它顯得更加煩瑣!

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

當使用你的反射代碼時,我得到一個對象引用沒有發送到 上的一個對象實例genericEnumerator = listEnumerator.MakeGenericMethod(listElemType); – BossRoss

+0

@BossRoss當你沒有任何改變的情況下運行我的示例代碼時,你不會得到那個錯誤。如果在更改代碼時發生錯誤,這是因爲在類中沒有稱爲「enumerateList」的靜態私有方法,因此'GetMethod()'返回null。您將需要更改該調用,以便爲要調用的方法指定適當的方法名稱和綁定標誌。 –

+0

我已經使用了反射代碼,它運作良好(當正確的方法是靜態的)謝謝你的幫助和良好的答案 – BossRoss

1

您必須查看kvp.Value的實施接口。這隻有通過反思纔有可能。

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); 

這樣,您可以在運行時確定集合的元素類型。但是,要檢索項目,最好使用非通用IEnumerable接口,因爲您不需要(昂貴的)反射開銷。檢查IEnumerable也是一個很好的起點,所以如果kvp.Value根本不是集合,那麼檢查元素類型沒有太大意義。

+0

'GetGenericTypeArguments'不可訪問,您應該使用'GetGenericArguments'。順便說一句,你的工作正常。 – Teejay

1

這適用於大多數類型的可枚舉:

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

對於集合(例如'HashSet '),這不起作用。 – Georg

+0

@Georg是的,我寫了**最** ** – Teejay

+0

@我用它來列表 – Teejay

相關問題