将泛型getter表达式转换为对象getter表达式

本文关键字:getter 表达式 对象 转换 泛型 | 更新日期: 2023-09-27 18:04:01

我有一个这样的表达式getter:

var expression = () => SomeInstance.Nr;

它被传递给一个方法:

public void AddExpression<T>(Expression<Func<T>> func)

现在我要将泛型表达式转换为

Expression<Func<object>>

我甚至不确定我是否能做到。我尝试这样做:

var converted = Expression.Convert(func, typeof(object));
var objectExpression = Expression.Lambda<Func<object>>(Expression.Call(converted.Method), func.Parameters);

但是当我调用

var number = objectExpression.Compile()();

它不会返回属性值。

代码可以在这里测试:http://volatileread.com/utilitylibrary/snippetcompiler?id=25062

更新:似乎调用被包装了第二次:

var converted = Expression.Convert(func, typeof(object));
var objectExpression = Expression.Lambda<Func<object>>(Expression.Call(converted.Method), func.Parameters);
var anotherDelegate = objectExpression.Compile().Invoke(); // would have expected the value here
var value = ((Delegate)anotherDelegate).DynamicInvoke(); // this now does return the value

将泛型getter表达式转换为对象getter表达式

不要在委托上使用Expression.Call -它只是用来调用方法的。相反,您希望使用Expression.Invoke,它专门用于调用委托。此外,执行表达式树的关键是包装—调用内部委托,转换结果,并将其包装在lambda中:

Expression<Func<int>> inputExpression = () => 42;
var newLambda = 
    Expression.Lambda<Func<object>>
    (
        Expression.Convert
        (
            Expression.Invoke(inputExpression), 
            typeof(object)
        )
    );
Console.WriteLine(newLambda.Compile()()); // Prints 42.

newLambdaExpression<Func<object>>,调用它会得到42,正如您所期望的那样。

当然,这使得使其成为扩展方法变得相当容易(尽管如果需要的话,您可能希望使用模板来生成所有不同的Func<...>重载)。

注意这只会工作,如果不管你使用LINQ提供程序实际上支持Invoke——在这种情况下,这不是一个问题,因为λ编译器可以处理它,但是如果你需要使用例如EntityFramework这样,你需要采取一种稍微不同的方式——你需要打开内λ,转换内部λ的身体,然后再把它在另一个λ。对于无参数表达式,这相当简单:

Expression<Func<int>> inputExpression = () => 42;
var newLambda = 
  Expression.Lambda<Func<object>>
  (
    Expression.Convert(inputExpression.Body, typeof(object))
  );
Console.WriteLine(newLambda.Compile()()); // Prints 42.
另外,为了完整起见,请注意,这只在内部表达式确实是常量表达式而不是引号的情况下才起作用——但如果您需要引号的嵌套表达式,您可能不会问这个问题:D