32

有没有办法使用反射对集合进行迭代(最好通过 foreach)?我正在使用反射迭代对象中的属性,当程序到达一个集合类型时,我希望它迭代集合的内容并能够访问集合中的对象。

目前,我在所有属性上都设置了一个属性,在作为集合的属性上将 IsCollection 标志设置为 true。我的代码检查这个标志,如果它是真的,它使用反射获取类型。有没有办法在集合上以某种方式调用 GetEnumerator 或 Items 以便能够迭代项目?

4

9 回答 9

36

我遇到了这个问题,但我没有使用反射,而是检查它是否是 IEnumerable。所有集合都实现了这一点。

if (item is IEnumerable)
{
    foreach (object o in (item as IEnumerable))
    {

    }
} else {
   // reflect over item
}
于 2008-09-19T19:12:19.263 回答
31

我尝试使用 Darren 建议的类似技术,但请注意,不仅仅是集合实现 IEnumerable。string例如也是 IEnumerable 并将遍历字符。

这是我用来确定对象是否为集合的小函数(这也是可枚举的,因为 ICollection 也是 IEnumerable)。

    public bool isCollection(object o)
    {
        return typeof(ICollection).IsAssignableFrom(o.GetType())
            || typeof(ICollection<>).IsAssignableFrom(o.GetType());
    }
于 2009-02-14T15:06:43.900 回答
8

只需获取属性的值,然后将其转换为 IEnumerable。这里有一些(未经测试的)代码可以给你一个想法:

ClassWithListProperty obj = new ClassWithListProperty();
obj.List.Add(1);
obj.List.Add(2);
obj.List.Add(3);

Type type = obj.GetType();
PropertyInfo listProperty = type.GetProperty("List", BindingFlags.Public);
IEnumerable listObject = (IEnumerable) listProperty.GetValue(obj, null);

foreach (int i in listObject)
    Console.Write(i); // should print out 123
于 2008-09-19T19:22:24.030 回答
3

仅供参考,可能会对某人有所帮助...我有一个带有嵌套类的类和一些其他类的集合。我想保存类的属性值以及嵌套类和类集合。我的代码如下:

 public void LogObject(object obj, int indent)
    {
        if (obj == null) return;
        string indentString = new string(' ', indent);
        Type objType = obj.GetType();
        PropertyInfo[] properties = objType.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            Type tColl = typeof(ICollection<>);
            Type t = property.PropertyType;
            string name = property.Name;


            object propValue = property.GetValue(obj, null); 
            //check for nested classes as properties
            if (property.PropertyType.Assembly == objType.Assembly)
            {
                string _result = string.Format("{0}{1}:", indentString, property.Name);
                log.Info(_result);
                LogObject(propValue, indent + 2);
            }
            else
            {
                string _result = string.Format("{0}{1}: {2}", indentString, property.Name, propValue);
                log.Info(_result);
            }

            //check for collection
            if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) ||
                t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl))
            {
                //var get = property.GetGetMethod();
                IEnumerable listObject = (IEnumerable)property.GetValue(obj, null);
                if (listObject != null)
                {
                    foreach (object o in listObject)
                    {
                        LogObject(o, indent + 2);
                    }
                }
            }
        }
    }

一个调用这个函数

LogObject(obj, 0);

但是,我的类中有一些结构,我需要弄清楚如何获取它们的值。Moreoevr,我有一些清单。我也需要获得它们的价值......如果我更新我的代码,我会发布。

于 2012-05-23T19:16:39.143 回答
2

您可能做的最好的事情是检查对象是否实现了某些集合接口——可能 IEnumerable 就是您所需要的。然后只需调用对象的 GetEnumerator(),并使用 IEnumerator.MoveNext() 和 IEnumerator.Current 来处理集合。

如果集合没有实现这些接口,这对您没有帮助,但如果是这种情况,我想它并不是一个真正的集合。

于 2008-09-19T19:11:48.597 回答
0

当您使用反射时,您不一定使用该对象的实例。您必须创建一个能够遍历对象属性的类型的实例。因此,如果您使用反射,请使用 ConstructorInfo.Invoke() (?) 方法来创建新实例或指向该类型的实例。

于 2008-09-19T19:11:35.460 回答
0

一种相当直接的方法是将对象类型转换为集合并直接使用它。

于 2008-09-19T19:14:15.297 回答
0

我会看看 Type.FindInterfaces 方法。这可以过滤掉给定类型实现的接口。如在 PropertyInfo.PropertyType.FindInterfaces(filterMethod, filterObjects) 中。您可以按 IEnumerable 进行过滤,并查看是否返回任何结果。MSDN 在方法文档中有一个很好的例子。

于 2009-02-20T16:17:13.217 回答
0

如果您不使用对象的实例,而是使用类型,则可以使用以下内容:

// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
于 2010-06-10T07:37:09.640 回答