首页 > 解决方案 > 如何在 C# 中从 lambda 引用方法参数

问题描述

给定这个 lambda

public static Expression<Func<int, int>> Add(int add)
{
    return x => x + add;
}

它创建以下调试输出:

.Lambda #Lambda1<System.Func`2[System.Int32,System.Int32]>(System.Int32 $x) {
    $x + .Constant<ConsoleAppExpressionTest.Program+<>c__DisplayClass1_0>(ConsoleAppExpressionTest.Program+<>c__DisplayClass1_0).add
}

调试视图显示该字段add在常量内寻址。

如何动态创建相同的 lambda?

我试过的:

public static Expression<Func<int, int>> Add(int add)
{
    var x = Expression.Parameter(typeof(int), "x");
    var expr = Expression.Add(x, Expression.Field(Expression.Constant(null), "add"));

    return Expression.Lambda<Func<int, int>>(expr, x);
}

导致System.ArgumentException: 'Instance field 'add' is not defined for type 'System.Object''.

直接使用参数作为常量只会创建值的副本:Expression.Constant(add)

标签: c#lambdareflection

解决方案


诀窍是使用简单的Expression.Constant(add)or Expression.Constant(add, typeof(int))(明确表达类型是个好主意,特别是如果值可以是null)。


至于使用现场方法:你不能,你的第一个版本中的常规编译器也不能。编译器所做的是生成一个闭包,并使用它,

var obj = new SomeGeneratedClosureType();
obj.add = add; // and now *all* mentions of "add" use obj.add instead

并使用该对象作为常量:

var expr = Expression.Add(x, Expression.Field(Expression.Constant(obj), "add"));

SomeGeneratedClosureTypeConsoleAppExpressionTest.Program+<>c__DisplayClass1_0您的问题代码中的。

但是,不这样做会更高效,更直接 ,直接使用该值即可。编译器不能这样做,因为它需要对值的行为方式保持一定的保证,如果你直接使用这个值是不可能的。


推荐阅读