在父表达式中重用表达式结果

Reusing Expression result in parent Expression

假设我有一个函数根据规则 "ab + a" 组合两个子 Expression(所有 Expression 将 return 和 int):

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

如果我编译 运行 结果 Expression,编译器是否足够聪明,知道参数 a 不需要在内部显式求值两次? abExpression 树可能非常冗长且昂贵。

如果结果 Expression 实际上会对 a 求值两次,那么缓存值以供重用的最佳方法是什么?

例如,我想知道这是否可行(但我不清楚 ConstantExpression 是否可以保存 运行 时间确定的 Expression 结果:

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

注意:我知道这可以重构为 "a(b+1)" 但我原来的问题仍然存在,因为我有其他组合可以重复使用 ab '被类似地分解。例如,"a2 + ab + b2".

是的,如果是原始表达式,它将被计算两次,但您可以缓存它。 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