将.net Func <T>转换为.net Expression <Func <T >>

Dav*_*ron 113 .net c# lambda expression func

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

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

public void SomewhereElse()
{
    GimmeExpression(() => thing.DoStuff());
}
Run Code Online (Sandbox Code Playgroud)

但我想将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());
}
Run Code Online (Sandbox Code Playgroud)

不起作用的行给了我编译时错误Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'.显式强制转换无法解决问题.我有一个设施可以做到这一点吗?

Meh*_*ari 99

哦,这根本不容易.Func<T>代表一般delegate而非表达.如果你有任何办法可以这样做(由于编译器完成了优化和其他事情,一些数据可能会被丢弃,因此可能无法获得原始表达式),它会在瞬间拆卸IL并推断表达(这绝不容易).将lambda表达式作为data(Expression<Func<T>>)处理是编译器完成的一项神奇工具(基本上编译器在代码中构建表达式树而不是将其编译为IL).

相关事实

这就是为什么将lambdas推向极端的语言(如Lisp)通常更容易实现为解释器.在这些语言中,代码和数据本质上是相同的(即使在运行时),但是我们的芯片无法理解这种形式的代码,因此我们必须通过在其上构建理解它的解释器来模拟这样的机器(在某种程度上(由C#做出的选择)牺牲权力(代码将不再完全等于数据).在C#中,编译器通过允许lambdas 在编译时被解释为code(Func<T>)和data(Expression<Func<T>>),给出了将代码视为数据的错觉.

  • @mheyman那会创建关于你的包装器动作的新的"表达式",但它没有关于`dangerousCall`委托内部的表达式树信息. (9认同)
  • Lisp不必被解释,它可以很容易地编译.宏必须在编译时扩展,如果你想支持`eval`,你需要启动编译器,但除此之外,完全没有问题. (3认同)
  • "Expression <Func <T >> DangerousExpression =()=> dangerousCall();" 不简单? (2认同)

小智 29

    private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)  
    {  
        return x => f(x);  
    } 
Run Code Online (Sandbox Code Playgroud)

  • @DaveCameron - 没有.见上面的答案 - 已编译的`Func`将隐藏在一个新的表达式中.这只是在代码上添加了一层数据; 你可以遍历一层只是为了找到你的参数`f`而没有进一步的细节,所以你就在你开始的地方. (6认同)

Dav*_*ier 21

你可能应该做的是转变方法.接受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());
}
Run Code Online (Sandbox Code Playgroud)

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


Ste*_*ock 7

你可以通过.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());
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ell 6

如果有时需要表达式,有时需要委托,则有两种选择:

  • 有不同的方法(每一种)
  • 始终接受Expression<...>版本,.Compile().Invoke(...)如果您需要委托,则仅接受版本。显然,这是有代价的。


Sag*_*agi 6

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)
    } 
}
Run Code Online (Sandbox Code Playgroud)


Dmi*_*gin 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);
        }
Run Code Online (Sandbox Code Playgroud)