通过反射访问集合

时间:2020-03-06 14:27:24  来源:igfitidea点击:

有没有一种方法可以使用反射来迭代(最好是通过foreach)整个集合?我正在使用反射来遍历对象中的属性,当程序进入到一个集合类型时,我希望它可以遍历集合的内容并能够访问集合中的对象。

目前,我在所有属性上都设置了一个属性,并且在作为集合的属性上将IsCollection标志设置为true。我的代码检查此标志,如果为true,则使用反射获取Type。有没有办法以某种方式在集合上调用GetEnumerator或者Items以便能够遍历这些item?

解决方案

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

我们可能最好的办法是检查对象是否实现了某些集合接口,而IEnumerable可能就是我们所需要的全部。然后,只需调用对象的GetEnumerator()并使用IEnumerator.MoveNext()和IEnumerator.Current即可遍历整个集合。

如果该集合没有实现这些接口,这将无济于事,但是我想,如果真是这样,那么它并不是真正的集合。

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

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

    }
} else {
   // reflect over item
}

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

只需获取属性的值,然后将其转换为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

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

这是一个小函数,用于确定对象是否为集合(由于ICollection也是IEnumerable,因此也可以枚举)。

public bool isCollection(object o)
    {
        return typeof(ICollection).IsAssignableFrom(o.GetType())
            || typeof(ICollection<>).IsAssignableFrom(o.GetType());
    }

我将看一下Type.FindInterfaces方法。这样可以过滤出由给定类型实现的接口。就像在PropertyInfo.PropertyType.FindInterfaces(filterMethod,filterObjects)中一样。我们可以按IEnumerable进行筛选,并查看是否返回任何结果。 MSDN在方法文档中有一个很好的例子。

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

// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}