如何调用System.Linq.Enumerable.Count&lt;&gt;在IEnumerable <t>上使用Reflection?</t>

时间:2010-08-23 09:01:53

标签: c# generics reflection collections ienumerable

我有一堆IEnumerable集合,确切的数量和类型经常更改(由于自动代码生成)。

它看起来像这样:

public class MyCollections {
    public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection;
    public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection;
    ...

在运行时,我想确定每个Type及其计数,而不必在每次代码生成后重写代码。所以我正在寻找使用反射的通用方法。我正在寻找的结果是:

MyType: 23
OtherType: 42

我的问题是我无法想象如何正确调用Count方法。以下是我到目前为止的情况:

        // Handle to the Count method of System.Linq.Enumerable
        MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethod("Count", new Type[] { typeof(IEnumerable<>) });

        PropertyInfo[] properties = typeof(MyCollections).GetProperties();
        foreach (PropertyInfo property in properties)
        {
            Type propertyType = property.PropertyType;
            if (propertyType.IsGenericType)
            {
                Type genericType = propertyType.GetGenericTypeDefinition();
                if (genericType == typeof(IEnumerable<>))
                {
                    // access the collection property
                    object collection = property.GetValue(someInstanceOfMyCollections, null);

                    // access the type of the generic collection
                    Type genericArgument = propertyType.GetGenericArguments()[0];

                    // make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection
                    MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument);

                    // invoke Count method (this fails)
                    object count = localCountMethodInfo.Invoke(collection, null);

                    System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count);
                }
            }
        }

4 个答案:

答案 0 :(得分:3)

这将涉及一些MakeGenericMethod - 并且很多反射通常。就个人而言,在这种情况下,我很想通过抛弃泛型来简化:

public static int Count(IEnumerable data) {
    ICollection list = data as ICollection;
    if(list != null) return list.Count;
    int count = 0;
    IEnumerator iter = data.GetEnumerator();
    using(iter as IDisposable) {
        while(iter.MoveNext()) count++;
    }
    return count;
}

即使通过反射获取,也可以轻易地转换为非泛型IEnumerable

答案 1 :(得分:3)

如果你坚持不懈地去做; p

的变化:

  • 如何获取通用方法的countMethodInfo
  • Invoke的参数

代码(注意obj是我MyCollections}的实例:

    MethodInfo countMethodInfo = typeof (System.Linq.Enumerable).GetMethods().Single(
        method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1);

    PropertyInfo[] properties = typeof(MyCollections).GetProperties();
    foreach (PropertyInfo property in properties)
    {
        Type propertyType = property.PropertyType;
        if (propertyType.IsGenericType)
        {
            Type genericType = propertyType.GetGenericTypeDefinition();
            if (genericType == typeof(IEnumerable<>))
            {
                // access the collection property
                object collection = property.GetValue(obj, null);

                // access the type of the generic collection
                Type genericArgument = propertyType.GetGenericArguments()[0];

                // make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection
                MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument);

                // invoke Count method (this fails)
                object count = localCountMethodInfo.Invoke(null, new object[] {collection});

                System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count);
            }
        }
    }

答案 2 :(得分:2)

到现在为止,这个问题已经得到了回答,但是我想向你们展示一个“调用通用扩展方法”的修剪版 - 我认为相当简单的版本,可以用来反思地调用Count

// get Enumerable (which holds the extension methods)
Type enumerableT = typeof(Enumerable);

// get the Count-method (there are only two, you can check the parameter-count as in above 
// to be certain. Here we know it's the first, so I use the first:
MemberInfo member = enumerableT.GetMember("Count")[0];

// create the generic method (instead of int, replace with typeof(yourtype) in your code)
MethodInfo method = ((MethodInfo) member).MakeGenericMethod(typeof(int));

// invoke now becomes trivial
int count = (int)method.Invoke(null, new object[] { yourcollection });

以上是有效的,因为您不需要使用IEnumerable<>的泛型类型来调用Count,这是Enumerable的扩展并且需要参数IEnumerable<T>作为第一个参数(它是一个扩展名),但您不需要指定它。

请注意,从您的问题的阅读中,在我看来,您实际上应该为您的类型使用泛型,这会将类型安全性添加回您的项目中,并且仍允许您使用Count或其他任何内容。毕竟,有一件事是肯定的,都是Enumerable,对吧?如果是这样,谁需要反思?

答案 3 :(得分:1)

var count = System.Linq.Enumerable.Count(theCollection);

编辑:你说它是生成的,所以你不能仅通过调用Count()来生成属性吗?

public class MyCollections
{
    public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection;
    public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection;

    public int CountSomeTypeCollection
    {
        get { return this.SomeTypeCollection.Count(); }
    }

    ...