如何通过Expression.Call对IList<Item>用自定义函数实现筛选?
解决方案
要实现通过表达式树构建Items.Where(Foo)的等效逻辑,你需要构造对Enumerable.Where静态方法的调用表达式,而非仅针对单个Item调用Foo。以下是具体修改方案:
核心问题分析
原代码仅生成了单个Item调用Foo的表达式,未完成Where方法的整体调用逻辑。我们需要:
- 正确获取
Enumerable.Where的泛型重载方法 - 构建筛选谓词(即
Foo方法对应的委托/表达式) - 组装完整的
Where调用表达式
方案1:通过Lambda表达式构造谓词
此方式等价于Items.Where(item => Foo(item)),与Items.Where(Foo)效果一致:
using System.Linq.Expressions; using System.Linq; public static Expression BuildExpression(IList<Item> items) { // 1. 定义Where方法的输入参数表达式(代表每个Item元素) var itemParam = Expression.Parameter(typeof(Item), "item"); // 2. 构造调用Foo方法的表达式:Foo(item) // 替换YourClass为实际包含Foo方法的类名 var fooCallExpr = Expression.Call(typeof(YourClass), "Foo", Type.EmptyTypes, itemParam); // 3. 构建筛选谓词的Lambda表达式:item => Foo(item) var predicateLambda = Expression.Lambda<Func<Item, bool>>(fooCallExpr, itemParam); // 4. 获取Enumerable.Where的泛型方法(适配IEnumerable<Item>和Func<Item,bool>的重载) var whereMethod = typeof(Enumerable).GetMethod( "Where", new[] { typeof(IEnumerable<Item>), typeof(Func<Item, bool>) } ); // 5. 构造完整的Where调用表达式:items.Where(predicateLambda) return Expression.Call( null, // 静态方法无需实例参数 whereMethod, Expression.Constant(items), predicateLambda ); } // 保持原Foo方法签名即可 private static bool Foo(Item item) { return item.Name.Equals("..."); }
方案2:直接传递Foo方法组(与Items.Where(Foo)完全一致)
如果希望严格匹配Items.Where(Foo)的写法(直接传递方法组),可以将Foo转换为Func<Item,bool>委托常量:
using System.Linq.Expressions; using System.Linq; using System.Reflection; public static Expression BuildExpression(IList<Item> items) { // 1. 获取Foo方法的MethodInfo(替换YourClass为实际类名) var fooMethod = typeof(YourClass).GetMethod( "Foo", BindingFlags.Static | BindingFlags.NonPublic // 根据Foo的访问修饰符调整 ); // 2. 将Foo方法转换为Func<Item,bool>委托 var fooDelegate = Delegate.CreateDelegate(typeof(Func<Item, bool>), fooMethod); // 3. 获取Enumerable.Where的泛型重载 var whereMethod = typeof(Enumerable).GetMethods() .First(m => m.Name == "Where" && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(Item)); // 4. 构造Where调用表达式 return Expression.Call( null, whereMethod, Expression.Constant(items), Expression.Constant(fooDelegate) ); }
关键注意事项
- 替换代码中的
YourClass为实际包含Foo方法的类名称 - 如果
Foo是实例方法,需调整Expression.Call的实例参数(传入实例的表达式),并修改GetMethod的绑定标志 - 确保
Foo方法的签名为bool Foo(Item item),否则需同步调整委托类型和表达式参数
内容的提问来源于stack exchange,提问作者styx
相关产品推荐
相关产品推荐

