温馨提示:本文翻译自stackoverflow.com,查看原文请点击:c# - Partial evaluation of expressions
c# linq-expressions

c# - 表达式的部分评估

发布于 2020-03-31 23:15:50

我想将一个方法调用捕获为一个表达式,但是要事先以类型安全和IDE友好的方式评估其参数。

采取以下示例类:

class Test
{
    public string SomeMethod(int a, string b) { ... }
}

我现在想将调用传递SomeMethod给接收的函数Expression

HandleMethodCall<Test>(test => test.SomeMethod("string".Length, 5.ToString()));

现在HandleMethodCall有签名

static void HandleMethodCall<T>(Expression<Func<T, object>> expr)

不幸的是,在这种情况下,传递给的表达式HandleMethodCall不仅包含test.SomeMethod(),而且还包含参数的表达式树。

一种解决方法是将方法调用及其参数拆分为的不同参数HandleMethodCall,但是这样做的代价是失去了编译时的类型安全性以及IDE对显示参数名称的支持。

有什么方法可以使编译器在将表达式的各个部分放入Expression对象之前对其求值

或者,是否可以手动调用方法参数的表达式树评估?

查看更多

提问者
Jan Wichelmann
被浏览
9
Jan Wichelmann 2020-01-31 19:19

看起来不可能在编译时指定该表达式,但是可以在运行时使用来编译和评估表达式部分Expression.Lambda<>(...).Compile()()(请参阅文档)。

这导致以下实现(为简单起见,不进行错误检查):

public static void HandleMethodCall<T>(Expression<Func<T, object>> expr)
{
    // Extract method call
    var methodCall = (MethodCallExpression)expr.Body;

    // Run through expected arguments and evaluate the supplied ones
    var expectedArguments = methodCall.Method.GetParameters();
    for(int i = 0; i < expectedArguments.Length; ++i)
    {
        Console.Write(expectedArguments[i].Name + ": ");

        // Since we do not know the argument type in advance, we have to use Func<object>
        var argumentEvaluationExpression = Expression.Lambda<Func<object>>(Expression.Convert(methodCall.Arguments[i], typeof(object)));
        object argumentValue = argumentEvaluationExpression.Compile()();

        Console.WriteLine(argumentValue);
    }
}

调用此函数

HandleMethodCall<Test>(test => test.SomeMethod("string".Length, "1" + " 2 " + (1 + 2).ToString()));

输出预期结果

a: 6
b: 1 2 3

但是,我不确定这是否在所有情况下都有效。另外,由于此步骤,我希望这会非常慢Compile()

感谢@RyanWilson的有用评论,这使我得以找出解决方案!