使用方法调用,从lambda到Expression很容易......
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
但我想将Func转换为表达式,仅在极少数情况下......
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
不起作用的行给出了编译时错误Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
。显式强制转换无法解决问题。有这样的设施可以忽略我吗?
答案 0 :(得分:95)
Func<T>
代表通用delegate
,而不是表达式。如果你有任何办法可以这样做(由于编译器完成了优化和其他事情,一些数据可能会被丢弃,因此可能无法获得原始表达式),它会在瞬间拆卸IL并推断表达(这绝不容易)。将lambda表达式作为数据(Expression<Func<T>>
)处理是编译器完成的一项神奇工具(基本上编译器在代码中构建表达式树而不是将其编译为IL)。
这就是为什么将lambdas推向极端的语言(如Lisp)通常更容易实现为解释器。在这些语言中,代码和数据本质上是相同的(即使在运行时),但我们的芯片无法理解这种形式的代码,因此我们必须通过在顶部构建解释器来模拟这样的机器理解它(由Lisp做出的选择,如语言)或牺牲权力(代码将不再完全等于数据)在某种程度上(由C#做出的选择)。在C#中,编译器通过允许将lambdas解释为代码(Func<T>
)和数据(Expression<Func<T>>
,给出了将代码视为数据的错觉。 )在编译时。
答案 1 :(得分:25)
private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)
{
return x => f(x);
}
答案 2 :(得分:20)
你可能应该做的是转变方法。接受表达式&gt;,然后编译并运行。如果失败,您已经有了要查看的表达式。
public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
try
{
dangerousCall().Compile().Invoke();;
}
catch (Exception e)
{
// This next line does not work...
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
显然,你需要考虑这个的性能影响,并确定它是否真的需要你做。
答案 3 :(得分:6)
你可以通过.Compile()方法走另一条路 - 但不确定这对你有用:
public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
try
{
var expr = dangerousCall.Compile();
expr.Invoke();
}
catch (Exception e)
{
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
var thing = new Thing();
ContainTheDanger(() => thing.CrossTheStreams());
}
答案 4 :(得分:5)
NJection.LambdaConverter是一个将委托转换为表达式
的库public class Program
{
private static void Main(string[] args) {
var lambda = Lambda.TransformMethodTo<Func<string, int>>()
.From(() => Parse)
.ToLambda();
}
public static int Parse(string value) {
return int.Parse(value)
}
}
答案 5 :(得分:5)
Expression<Func<T>> ToExpression<T>(Func<T> call)
{
MethodCallExpression methodCall = call.Target == null
? Expression.Call(call.Method)
: Expression.Call(Expression.Constant(call.Target), call.Method);
return Expression.Lambda<Func<T>>(methodCall);
}
答案 6 :(得分:4)
如果您有时需要表达式并且有时需要委托,则有2个选项:
Expression<...>
版本,如果您想要代表,则只需.Compile().Invoke(...)
。显然这有成本。答案 7 :(得分:4)
来自Cecil Mono团队的JB Evain正在取得一些进展来实现这个
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
答案 8 :(得分:-1)
更改
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
要
// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();