Как вызвать System.Linq.Enumerable.Count <> в IEnumerable <T>, используя Reflection? - PullRequest
6 голосов
/ 23 августа 2010

У меня есть несколько коллекций IEnumerable, точное количество и типы которых часто изменяются (из-за автоматической генерации кода).

Это выглядит примерно так:во время выполнения я хочу определить каждый тип и его количество без необходимости переписывать код после каждого генерирования кода.Поэтому я ищу общий подход с использованием отражения.Результат, который я ищу, выглядит примерно так:

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 ]

3 голосов
/ 23 августа 2010

Если вы настаиваете на том, чтобы сделать это трудным путем; 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);
            }
        }
    }
3 голосов
/ 23 августа 2010

Это будет включать в себя немного 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 тривиально, даже если выбираете с помощью отражения.

2 голосов
/ 23 августа 2010

К настоящему времени на вопрос уже получен ответ, но я хотел бы представить вам урезанную - и я думаю, довольно тривиальную версию - "вызова общего метода расширения", который можно использовать дляinvoke 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, верно?Если это так, кому нужно отражение?

1 голос
/ 23 августа 2010
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(); }
    }

    ...
...