将.net Func <t>转换为.net表达式<func <t>&gt; </func <t> </t>

时间:2009-04-20 10:40:46

标签: c# .net lambda expression func

使用方法调用,从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>>'。显式强制转换无法解决问题。有这样的设施可以忽略我吗?

9 个答案:

答案 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个选项:

  • 有不同的方法(每个1个)
  • 始终接受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();