在父表达式中重用Expression结果

时间:2015-05-28 02:48:34

标签: c# expression-trees

假设我有一个功能,结合两个孩子Expression(所有Expression将返回int)根据规则" ab + a":

public Expression CustomCombine(Expression a, Expression b)
{
    Expression mult = Expression.Multiply(a, b);
    return Expression.Add(mult, a);
}

如果我编译并运行结果Expression,编译器是否足够聪明,知道参数a不需要在内部显式评估两次? Expressiona的{​​{1}}树可能非常冗长而且价格昂贵。

实际上,如果结果b会评估Expression两次,那么缓存该值以便重用的最佳方法是什么?

例如,我想知道这是否可行(但我不清楚a是否可以在运行时确定ConstantExpression结果):

Expression

注意:我知道这可以重构为" a(b + 1)"但我原来的问题仍然存在,因为我有其他组合,可能会以不同的方式重复使用public Expression CustomCombine(Expression a, Expression b) { Expression aVal = Expression.Constant(a); Expression mult = Expression.Multiply(aVal, b); return Expression.Add(mult, aVal); } a。例如," a 2 + ab + b 2 "。

1 个答案:

答案 0 :(得分:4)

是的,如果是原始表达式,它将被评估两次,但您可以缓存它。 Expression.Constant在这种情况下不起作用,因为它需要常量值,并且需要在表达式执行期间进行评估,但是您可以使用Expression.Variable声明新变量,然后使用Expression.Assign来保存a对该变量赋值,然后使用这些表达式声明Exression.Block,因为Variable需要自己的块。 如果需要,您可以对b变量执行相同的操作。

以下代码将生成这样的表达式:

(obj) => { int cached = obj.A; return cached*obj.B + cached; }

以下是示例代码:

using System;
using System.Linq.Expressions;

public class Program
{
    public static void Main(string[] args)
    {
        ParameterExpression param = Expression.Parameter(typeof(Test), "obj");
        Expression a = Expression.Property(param, "A");
        Expression b = Expression.Property(param, "B");
        Expression result = CustomCombine(a, b);

        var lambda = Expression.Lambda<Func<Test, int>>(result, new ParameterExpression[] { param });
        Func<Test, int> func = lambda.Compile();

        var obj = new Test();
        var val = func(obj);

        Console.WriteLine("Result is " + val);
    }

    private static Expression CustomCombine(Expression a, Expression b)
    {
        var variable = Expression.Variable(a.Type, "cached");
        var aVal = Expression.Assign(variable, a);

        var mult = Expression.Multiply(variable, b);
        var result = Expression.Add(mult, variable);
        // here we are making Block with variable declaration and assigment
        var block = Expression.Block(new ParameterExpression[]{variable}, aVal, result);
        return block;
    }
}

public class Test
{
    public int A
    {
        get
        {
            Console.WriteLine("Property A is accessed");
            return 42;
        }
    }

    public int B
    {
        get
        {
            return 1;
        }
    }
}

使用.NetFiddle示例 - https://dotnetfiddle.net/bfYVbv