如何调用 System.Linq.Enumerable.Count<>在 IEnumerable 上使用反射?

发布于 2024-09-15 17:38:45 字数 1918 浏览 2 评论 0 原文

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

它看起来像这样:

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

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

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

I have a bunch of IEnumerable Collections which exact number and types is subject of frequent changes (due to automatic code generation).

It looks something like this:

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

At runtime i want to determine each Type and it's count without having to rewrite the code after every code generation. So i am looking for a generic approach using reflection. The result i am looking for is something like:

MyType: 23
OtherType: 42

My problem is that i can't figure how to invoke the Count method properly. Here is what i have so far:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

千仐 2024-09-22 17:38:46

如果您坚持采用困难的方式;p

更改:

  • 如何获取通用方法的 countMethodInfo
  • 调用代码的参数

(注意 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);
            }
        }
    }

If you insist on doing it the hard way ;p

Changes:

  • how you obtain countMethodInfo for a generic method
  • the arguments to Invoke

Code (note obj is my instance of 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);
            }
        }
    }
梦里°也失望 2024-09-22 17:38:46

这将涉及一些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 。

That is going to involve some MakeGenericMethod - and a lot of reflection generally. Personally, I would be tempted to just simplify by ditching the generics in this case:

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

You can cast to the non-generic IEnumerable trivially, even if fetching via reflection.

清风无影 2024-09-22 17:38:46

现在,问题已经得到解答,但我想向您展示“调用通用扩展方法”的精简版本(我认为相当简单),它可用于调用 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 参数作为第一个参数(它是一个扩展),但您不需要指定它。

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

By now, the question has been answered, but I'd like to present you a trimmed down — and I think, rather trivial version — of the "calling a generic extension method", which can be used to invoke Count reflectively:

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

The above works, because you don't need to use the generic type of IEnumerable<> to be able to invoke Count, which is an extension of Enumerable and takes an argument of IEnumerable<T> as first param (it's an extension), but you don't need to specify that.

Note that, from the reading of your question, it seems to me that you should actually use generics for your types, which adds type safety back into your project and still allows you to use Count or whatever. After all, the one thing that's certain is that all are Enumerable, right? If so, who needs reflection?

只为一人 2024-09-22 17:38:46
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(); }
    }

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

Edit: you say it's generated though, so can you not just generate a properties with calls to 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(); }
    }

    ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文