【问题标题】:KeyValuePair<string,object> Is the Object Enumerable?KeyValuePair<string,object> 对象是可枚举的吗?
【发布时间】:2023-03-25 10:04:01
【问题描述】:

如何以编程方式确定键值对中的对象是否可枚举?

我需要知道值字段中的对象是列表还是数组。我应该能够确定对象是哪种可枚举类型(例如字符串列表或整数数组)

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

【问题讨论】:

    标签: c# key-value


    【解决方案1】:

    使用dynamic

    您可以利用 dynamic 关键字来执行此操作,但我认为这对您来说可能太慢了。

    但是,您可以这样做。这将为List&lt;T&gt;T[] 调用强类型enumerate() 方法,或者如果值既不是List 也不是数组,它将调用enumerate() 的重载,它只接受一个对象。

    我不完全确定这是您所追求的,但它确实为您提供了 KVP 列表中列表和数组的强类型枚举。

    请注意,根据您的规范,这仅考虑 List 和 Array 类型;其他可枚举类型(如字符串、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();
            }
        }
    }
    

    【讨论】:

    • 当使用你的反射代码时,我得到一个对象引用没有发送到行 genericEnumerator =listEnumerator.MakeGenericMethod(listElemType);
    • @BossRoss 当您运行我的示例代码而不做任何更改时,您不会收到该错误。如果在更改代码时出现该错误,那是因为类中没有名为“enumerateList”的静态私有方法,因此GetMethod() 返回 null。您将需要更改该调用,为您要调用的方法指定适当的方法名称和绑定标志。
    • 我已经使用了反射代码,效果很好(当正确的方法是静态的时候)谢谢你的帮助和好的答案
    【解决方案2】:

    您必须查看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根本不是一个集合,那么检查元素类型没有太大意义。

    【讨论】:

    • GetGenericTypeArguments 不可访问,您应该使用GetGenericArguments。顺便说一句,你的工作正常。
    【解决方案3】:

    这适用于大多数 Enumerable 类型:

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

    【讨论】:

    • @Georg 是的,我写了大部分
    • @ANeves 我用它来做列表
    • @ANeves 您必须在 System.Collections 中包含 using 语句
    • 真;我只有System.Collections.Generic
    • @Teejay 是的,但在我看来,集合是一个非常重要的集合类别(尽管微软显然有不同的看法)。您的方法仅对列表有效
    猜你喜欢
    • 2019-09-17
    • 1970-01-01
    • 2013-05-18
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 2013-12-16
    • 1970-01-01
    • 2011-06-06
    相关资源
    最近更新 更多