表达式的部分评估
Partial evaluation of expressions
我想将方法调用捕获为表达式,但事先以类型安全且 IDE 友好的方式评估其参数。
举下面的例子class:
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
对象之前对它们求值?
或者,是否可以手动调用方法参数的表达式树求值?
看起来不可能在编译时指定它,但是可以使用 Expression.Lambda<>(...).Compile()()
在运行时编译和计算表达式部分(参见 documentation)。
这导致以下实现(为简单起见没有错误检查):
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 的有用评论,让我找到了这个解决方案!
我想将方法调用捕获为表达式,但事先以类型安全且 IDE 友好的方式评估其参数。
举下面的例子class:
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
对象之前对它们求值?
或者,是否可以手动调用方法参数的表达式树求值?
看起来不可能在编译时指定它,但是可以使用 Expression.Lambda<>(...).Compile()()
在运行时编译和计算表达式部分(参见 documentation)。
这导致以下实现(为简单起见没有错误检查):
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 的有用评论,让我找到了这个解决方案!