将泛型 getter 表达式转换为对象 getter 表达式
Convert generic getter expression to object getter 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()();
它不会 return 属性 值。
代码可以在这里测试:
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
不要在委托上使用 Expression.Call
- 那只是用来调用方法的。相反,您想使用 Expression.Invoke
,它专门用于调用委托。此外,执行表达式树的关键是 wrapping - 调用内部委托,转换结果,并将其包装在 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.
newLambda
是 Expression<Func<object>>
并且调用它会给你 42
,如你所料。
当然,这使得将其作为扩展方法变得相当容易(尽管如果需要,您可能希望使用模板生成所有不同的 Func<...>
重载)。
请注意,只有当您使用的任何 LINQ 提供程序实际上 支持 Invoke
时,这才会起作用 - 在这种情况下,这不是问题,因为lambda 编译器可以处理它,但是如果您需要使用类似这样的东西,例如EntityFramework,你需要采取稍微不同的方法 - 你需要 unwrap 内部 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
我有这样的表达式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()();
它不会 return 属性 值。
代码可以在这里测试: 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
不要在委托上使用 Expression.Call
- 那只是用来调用方法的。相反,您想使用 Expression.Invoke
,它专门用于调用委托。此外,执行表达式树的关键是 wrapping - 调用内部委托,转换结果,并将其包装在 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.
newLambda
是 Expression<Func<object>>
并且调用它会给你 42
,如你所料。
当然,这使得将其作为扩展方法变得相当容易(尽管如果需要,您可能希望使用模板生成所有不同的 Func<...>
重载)。
请注意,只有当您使用的任何 LINQ 提供程序实际上 支持 Invoke
时,这才会起作用 - 在这种情况下,这不是问题,因为lambda 编译器可以处理它,但是如果您需要使用类似这样的东西,例如EntityFramework,你需要采取稍微不同的方法 - 你需要 unwrap 内部 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