获取任意IQueryable<T>上Count() LINQ扩展方法的MethodInfo

8

我有一个IQueryable<T>的源代码,并且我想要动态调用IQueryable<T>.Count()。

因此,我需要在IQueryable中声明的Count方法的MethodInfo。

以下是它在msdn上的签名(在IQueryable<>中):

public static int Count<TSource>(
    this IQueryable<TSource> source
)

这是我已经完成的程度:
Expression expr; //this is expression which holds my IQueryable<T>
MethodInfo mi = expr.Type.GetMethod("Count", BindingFlags.Static | BindingFlags.Public, null, new[] { expr.Type }, null); 

但是我的 mi 始终为空;

我也尝试过:

mi = typeof(IQueryable<>).GetMethod("Count", BindingFlags.Static | BindingFlags.Public, null, new[] { expr.Type }, null);

但是再次为 null。

我的最终目标是:

Expression.Call(mi, expr);

更新: 这是我获取 Sum 扩展方法的方式:
MethodInfo sum = typeof(Queryable).GetMethod("Sum", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(IQueryable<decimal>) }, null);

这个方法可以工作,但是这个Sum方法并不是泛型方法。虽然它是静态的。

2个回答

9
您需要提取 IQueryable<T> 类型的通用参数并使用它;此外,拥有该方法的类型不是 IQueryable<T>,而是 Queryable。思考一下,接口不能有静态方法 (好吧,如评论者所指出的,在 C# 中是可以的) :)

另外,因为它是一个泛型方法,您无法以您尝试的方式匹配参数:因为您需要传递泛型类型定义 IQuerable<TSource> — 而不是实际表达式中的泛型类型 IQueryable<int> 或其他。

相反,您只需查找名为“Count”的静态单参数版本的 Queryable 类型上的方法:

Type genericArgument = expr.GetGenericArguments()[0];

MethodInfo countMethod = typeof(Queryable)
              .GetMethods(BindingFlags.Static | BindingFlags.Public)
              //narrow the search before doing 'Single()'
              .Single(mi => mi.Name == "Count"
                         // this check technically not required, but more future proof
                         && mi.IsGenericMethodDefinition
                         && mi.GetParameters().Length == 1)
              .MakeGenericMethod(genericArgument);

//now you can bind to 'countMethod'

2017年3月7日更新 - 很明显,框架中的某些内容发生了变化,这导致了原始版本的代码示例无法工作 - 这是一个更新后应该可以工作的版本

进一步深入了解,该方法的签名为:

public static int Count<TSource>(
  this IQueryable<TSource> source
)

因此,虽然参数类型是IQueryable<TSource>,但它是泛型的类型 - 这就是为什么您需要深入研究您的IQueryable<TSource>表达式并抓住其泛型参数的原因。您也应该能够看到我在这里所说的参数的含义。

太棒了。好东西。感谢你的努力。非常感谢。 - Milos Mijatovic
实际上,接口可以拥有静态方法——只是在C#中不行,也不符合CLS标准 :) - Botz3000
1
当我调用typeof(Queryable).GetMethod("Count", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(IQueryable<>) }, null)时,我收到了null(使用System.LinqSystem.Reflection)。我错过了什么吗? - slartidan
@EugeneOsovetsky 如果你找到了比这里发布的解决方案更好的解决方案,那么你可以在自己的答案中发布它。将你自己的解决方案编辑到其他人的答案中是不合适的。 - Servy
这只是对现有答案的轻微修改。我对问题的理解不足以自己撰写完整的答案,也不想因为对基本上是别人答案的轻微修改而获得荣誉。然而,我认为我的编辑对那些试图使用此答案并遇到与我相同问题的人非常有帮助。我不明白删除我的代码片段如何帮助任何人。 - Eugene Osovetsky
显示剩余3条评论

4
让编译器为您获取方法。
Type genericArgument = expr.GetGenericArguments()[0];
var fakeExp = (Expression<Func<IQueryable<int>, int>>)(q => q.Count());
var mi = ((MethodCallExpression)fakeExp.Body).Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgument);

当我尝试使用Expression.Call(propEx, mi)时,它会给我一个错误:静态方法需要空实例,非静态方法需要非空实例。参数名称:实例。 - Roman
1
使用以MethodInfo为第一个参数的重载(https://msdn.microsoft.com/zh-cn/library/dd323922%28v=vs.110%29.aspx) Count是System.Linq.Queryable类中的静态扩展方法 ;) - MBoros
太棒了!现在它可以工作了!谢谢,兄弟!我更喜欢这个答案,因为对我来说更清晰。 - Roman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接