将.net Func <T>转换为.netexpression式<Func <T >>

使用方法调用从lambda转换为Expression很容易

public void GimmeExpression(Expression<Func<T>> expression) { ((MemberExpression)expression.Body).Member.Name; // "DoStuff" } public void SomewhereElse() { GimmeExpression(() => thing.DoStuff()); } 

但是我想把Func转换成expression式,只有在极less数情况下…

 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>>' 。 明确的转换不能解决这种情况。 有没有这样的设施可以忽略?

哦,这并不容易。 Func<T>表示一个通用的delegate而不是一个expression式。 如果有什么办法可以这样做的话(由于编译器做了优化和其他的事情,有些数据可能会被抛弃,所以可能不可能得到原始expression式),它将在运行中反编译IL并推断expression式(这绝非易事)。 将Lambdaexpression式作为数据处理( Expression<Func<T>> )是编译器完成的一项魔术(基本上,编译器在代码中构buildexpression式树,而不是将其编译为IL)。

相关的事实

这就是为什么把lambda推到极致的语言(比如Lisp)通常更容易作为解释器来实现。 在这些语言中,代码和数据本质上是相同的(即使在运行时 ),但是我们的芯片不能理解这种forms的代码,所以我们必须通过在其上面build立一个理解它的解释器来模拟这样的一个机器或者牺牲function(代码将不再完全等同于数据)在某种程度上(由C#所做的select)。 在C#中,编译器通过在编译时将lambda解释为代码Func<T> )和数据Expression<Func<T>> ),给出了将代码视为数据的错觉。

你可能应该做的,是把方法转过来。 进入expression式>,编译并运行。 如果失败,你已经有了expression式。

 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()); } 

显然你需要考虑这个的性能影响,并确定它是否是你真的需要做的事情。

  private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f) { return x => f(x); } 

你可以通过.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()); } 
  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); } 

如果你有时需要一个expression式,有时需要一个委托,你有两个select:

  • 有不同的方法(每个1)
  • 总是接受Expression<...>版本,只是.Compile().Invoke(...)如果你想要一个委托, .Compile().Invoke(...) 。 显然这有成本。

NJection.LambdaConverter是一个将代表转换为expression式的库

 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) } } 

更改

 // This next line does not work... Expression<Func<T>> DangerousExpression = dangerousCall; 

 // This next line works! Expression<Func<T>> DangerousExpression = () => dangerousCall();