2014-02-19 29 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