假设我有一个功能,结合两个孩子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
不需要在内部显式评估两次? Expression
和a
的{{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 "。
答案 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