如何快速获取Expression方法调用的目标对象?
如何快速获取表达式中的目标对象(无需编译表达式)
当然有办法跳过编译表达式的步骤,直接拿到目标对象——这才是解决你遇到的性能瓶颈的核心方案!
你已经通过基准测试发现了问题:Compile调用是最大的性能杀手,哪怕强类型的Func<object>.Invoke再快,也架不住编译表达式带来的巨大开销。而我们完全可以通过直接解析表达式树的结构来获取目标对象,根本不需要走编译这一步。
问题根源
当你写出Expression<Action> expression = () => o.ToString();这种捕获外部变量的Lambda时,编译器会自动生成一个闭包类,把变量o作为这个类的字段存储。此时表达式树里的methodCall.Object并不是直接的常量,而是一个指向闭包实例字段的MemberExpression。原来的代码通过编译表达式来获取值,相当于绕了一大圈,完全没必要。
无需编译的实现方案
我们可以递归解析表达式树,直接提取目标值:
public static object GetTargetFromExpression(Expression<Action> expression) { var methodCall = (MethodCallExpression)expression.Body; return ExtractValue(methodCall.Object); } private static object ExtractValue(Expression expression) { // 处理直接的常量表达式 if (expression is ConstantExpression constantExpr) { return constantExpr.Value; } // 处理闭包中的字段/属性访问(Lambda捕获变量的核心场景) if (expression is MemberExpression memberExpr) { // 先获取父表达式的值(比如闭包实例) var parentValue = ExtractValue(memberExpr.Expression); // 根据成员类型获取值:字段或属性 return memberExpr.Member switch { FieldInfo field => field.GetValue(parentValue), PropertyInfo prop => prop.GetValue(parentValue), _ => null }; } // 可以根据需要扩展支持其他表达式类型,比如ParameterExpression等 throw new NotSupportedException($"无法处理的表达式类型:{expression.GetType()}"); }
性能优势
这种方法的性能开销几乎可以忽略不计——它只是通过反射读取字段/属性值,完全跳过了表达式编译这个重量级操作。对比你的基准测试:
- 它的耗时会远低于
DelegateCompile和FuncCompile(这两个都是编译操作) - 甚至比
FuncInvoke还要快,因为连委托调用的开销都省了
验证示例
用你的场景测试一下:
object o = new object(); Expression<Action> expression = () => o.ToString(); object target = GetTargetFromExpression(expression); Console.WriteLine(target == o); // 输出 True
完全可以正确获取到目标对象,而且速度极快。
内容的提问来源于stack exchange,提问作者Thomas Flinkow
相关产品推荐
相关产品推荐

