【问题标题】:How to invoke System.Linq.Enumerable.Count<> on IEnumerable<T> using Reflection?如何使用反射在 IEnumerable<T> 上调用 System.Linq.Enumerable.Count<>?
【发布时间】:2011-04-02 13:01:08
【问题描述】:

我有一堆 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);
                }
            }
        }

【问题讨论】:

  • “MyCollections”是一种变量/字段/属性吗?你似乎同时使用它。
  • 您的计数 MethodInfo-reference 为空。修复它,代码可能会工作。
  • 对不起,给定的例子不准确。我已经修好了。
  • 我不会为 .NET 中的通用通配符提供什么。 . .
  • 在我阅读答案之前,谁能给我解释一下这个问题?

标签: c# generics reflection collections ienumerable


【解决方案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(); }
    }

    ...

【讨论】:

  • 抱歉,错过了编译时您不知道T这一事实。
  • MyCollections 会自动生成并经常被覆盖。这就是为什么我必须使用外部反射。我知道这很棘手,但应该有可能......
【解决方案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&lt;&gt; 的泛型类型就可以调用Count,它是Enumerable 的扩展,并将IEnumerable&lt;T&gt; 作为第一个参数(它是一个扩展),但您不需要指定。

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

【讨论】:

  • 反射的原因很简单:你标记为“你的类型”的东西在编译时是未知的(或者至少它经常变化)。
  • @embee:我明白了。但是如果它经常改变(它是生成的)它是泛型的理想选择。但老实说,我对你的情况了解得不够多,无法确定你的方法是否是一个好的考虑。
  • 可能我只是不太了解泛型,无法遵循您的建议,抱歉。在这种情况下,您必须将 MyCollections 视为理所当然。
  • @embee:我添加了一个注释,希望它能澄清一点。我的理解是,您当前所做的超出了调用静态方法所需的工作,该方法包含您在编译时不知道该类型的类型的集合。但也许我完全误解了? :)
  • 在新的阅读中,我发现我非常关注已经说过的内容。我被你的 foreach 循环分心了。对不起。因此,上面的内容应该是:这就是调用泛型方法Count 所需要的全部内容。您的循环是您的基本逻辑的一部分。
【解决方案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

【讨论】:

  • 当然,这是个好主意。但我正在寻找解决我在 MakeGenericMethod 中的缺陷的方法。
  • 我们不能只使用foreach 而不是data,还是需要明确地处理枚举数?
  • 为什么有些人“害怕”通过转换为 IListIEnumerable 而失去泛型?
  • @AakashM, Marc,方法避免了对Current 的不必要调用,这可能(取决于枚举器)很昂贵并且可能不会被优化掉。 @Marc,我将第一个测试作为 ICollection 的测试,而不是 IList,因为 ICollection 是为 IList 定义 Count 属性的地方,这样您将使用内置的(并且可能更有效)计数。
  • @Jon Hanna: 如果Current 很贵,那你就错了:)
【解决方案4】:

如果你坚持以艰难的方式去做;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);
            }
        }
    }

【讨论】:

  • 非常感谢!在检查了您的回复后,我突然明白了我的错误。事实上,只需要调整 Invoke 的参数(当然!),然后我的所有原始实现都按预期工作。即使你的 Linq 检索 countMethodInfo 的方法很艰难,看起来也很性感,对我来说感觉有点复杂 ;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-21
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2015-03-12
相关资源
最近更新 更多