将通用getter表达式转换为对象getter表达式

时间:2015-07-22 07:55:49

标签: c# lambda expression

我有一个像这样的表达式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

1 个答案:

答案 0 :(得分:3)

不要在委托上使用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时才会起作用 - 在这种情况下,这不是问题,因为lambda编译器可以处理它,但如果你需要使用像这样的东西EntityFramework,你需要采取一种稍微不同的方法 - 你需要展开内部lambda,转换内部lambda的主体,然后再将它包装在另一个lambda中。对于无参数表达式,这很容易:

Expression<Func<int>> inputExpression = () => 42;

var newLambda = 
  Expression.Lambda<Func<object>>
  (
    Expression.Convert(inputExpression.Body, typeof(object))
  );

Console.WriteLine(newLambda.Compile()()); // Prints 42.

另外,为了完整性,请注意,这仅在内部表达式实际上是常量表达式而不是引用时才有效 - 但如果需要引用嵌套表达式,则可能不会问这个问题:D