Reusing the result of an expression in a parent expression

Let's say I have a function that merges two children Expression

(all Expression

will return int

) according to the "ab + a" rule:

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

      

If I compile and run the resulting one Expression

, is it enough for the compiler to know that the argument a

should not be explicitly double checked internally? Trees Expression

for a

and b

can be quite long and expensive.

If in fact the resultant Expression

will evaluate twice a

, then what is the best way to cache the value for reuse?

For example, I was wondering if maybe this would work (but it is not clear to me if it can ConstantExpression

get the result Expression

determined at runtime):

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

      

Note. I know this could be refactored as "a (b + 1)", but my original question holds true as I have other combinations that can be reused a

or b

many times over in ways that cannot be similar. For example, "a 2 + ab + b 2 ".

+3


source to share


1 answer


Yes, in the case of the original expression, it will be evaluated twice, but you can cache it. Expression.Constant

won't work in this case as it requires a constant value and needs to be evaluated at runtime of the expression, but you can use Expression.Variable

to declare a new variable and then Expression.Assign

store the value a

for that variable and then declare Exression.Block

with those expressions as it Variable

requires a custom block ... You can do the same for the variable b

if you like.

The following code will generate an expression like this:

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

      



Here's some sample code:

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

      

And a working .NetFiddle sample is https://dotnetfiddle.net/bfYVbv

+4


source







All Articles