在父表达式中重用表达式结果
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
不需要在内部显式求值两次? a
和 b
的 Expression
树可能非常冗长且昂贵。
如果结果 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)" 但我原来的问题仍然存在,因为我有其他组合可以重复使用 a
或 b
'被类似地分解。例如,"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
假设我有一个函数根据规则 "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
不需要在内部显式求值两次? a
和 b
的 Expression
树可能非常冗长且昂贵。
如果结果 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)" 但我原来的问题仍然存在,因为我有其他组合可以重复使用 a
或 b
'被类似地分解。例如,"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