我想将一个方法调用捕获为一个表达式,但是要事先以类型安全和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
对象之前对其求值?
或者,是否可以手动调用方法参数的表达式树评估?
看起来不可能在编译时指定该表达式,但是可以在运行时使用来编译和评估表达式部分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的有用评论,这使我得以找出解决方案!