gpt4 book ai didi

c# - 创建可在运行时计算的表达式

转载 作者:太空宇宙 更新时间:2023-11-03 13:30:21 25 4
gpt4 key购买 nike

我有一个产品列表,我需要针对这些产品创建表达式树,这些表达式树可以持久保存并在以后检索和执行。这是针对客户端计算构建器的。

我是表达式的新手,虽然我已经阅读了相当多的文档,但这里的学习曲线有点陡峭。我想要的是能够开始累积 PropertyExpression 和 Operand 对。这是我目前所掌握的,但我不确定我的结构是否正确。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
public enum ProductType { Perishable, Fixed, Miscellaneous }
public enum OperandType { Addition, Sunbtraction, Multiplication, Division }

public class Product
{
public string Name { get; set; }
public ProductType Type { get; set; }
public float Price { get; set; }
}

public class Configuration
{
public Dictionary<string, float> Dictionary { get; set; }
}

public class Operand
{
public OperandType Type { get; set; }
}

public class CalculationPair<TEntityType, TProperty>
where TEntityType: class
where TProperty: struct
{
public ValueTypeProperty<TEntityType, TProperty> Left { get; set; }
public Operand Operand { get; set; }
public ValueTypeProperty<TEntityType, TProperty> Right { get; set; }

// How to specify TResult as an [out] parameter?
public TResult Calculate<TResult> ()
where TResult: struct
{
TResult result = default(TResult);

if (this.Operand.Type == OperandType.Multiplication)
{
// How to execute the expression?
//result = this.Left * this.Right;
}

return (result);
}
}

public class ValueTypeProperty<TEntityType, TProperty>
where TEntityType: class
where TProperty: struct
{
public string Name { get; set; }

public Expression<Func<TEntityType, TProperty>> PropertyExpression { get; set; }
}

public class ProductPriceProperty:
ValueTypeProperty<Product, float>
{
}

public static class Program
{
public static void Main ()
{
Configuration config = new Configuration();
List<Product> products = new List<Product>();

config.Dictionary.Add("ExportFactor", 80);
config.Dictionary.Add("ChannelMargin", 100);

products.Add(new Product() { Name = "1", Type = ProductType.Fixed, Price = 10 });
products.Add(new Product() { Name = "2", Type = ProductType.Miscellaneous, Price = 20 });
products.Add(new Product() { Name = "3", Type = ProductType.Perishable, Price = 30 });

foreach (var product in products)
{
if (product.Type == ProductType.Fixed)
{
CalculationPair<Product, float> calculation = new CalculationPair<Product, float>()
{
Left = new ProductPriceProperty() { Name = "Product Price", PropertyExpression = (entity => entity.Price) },
Operand = new Operand() { Type = OperandType.Multiplication },
Right = new ProductPriceProperty() { Name = "ExportFactor", PropertyExpression = (entity => config.Dictionary ["ExportFactor"]) },
};

// Calculation needs to be persisted to be retrieved later.
// ???!

// Once calculation has been reconstruction from the persistence layer, it needs to be executed.
product.Price = calculation.Calculate<float>();
}
}
}
}
}

更新:按优先级排序,这是我正在努力解决的问题:

  • 如何执行 CalculationPair.Calculate<TReult>() 中的表达式功能?
  • 如何将 TResult 指定为 CalculationPair.Calculate<TReult>() 中的 [out] 参数功能?
  • 如何持久化计算表达式并在以后检索它?

最佳答案

正如 Jon 所说,你可以使用通常的表达式树,或者你可以像下面的代码那样创建闭包匿名方法:

public class CalculationPair<TEntityType, TProperty>
where TEntityType : class
where TProperty : struct
{
// not sure that first three properties are needed here
public ValueTypeProperty<TEntityType, TProperty> Left { get; set; }
public Operand Operand { get; set; }
public ValueTypeProperty<TEntityType, TProperty> Right { get; set; }

// closure method
public Func<TEntityType, TProperty> Calculator { get; set; }
}

这是使用它的 Main 方法的一部分:

foreach (var product in products)
{
if (product.Type == ProductType.Fixed)
{
CalculationPair<Product, float> calculation = new CalculationPair<Product, float>()
{
Left = new ProductPriceProperty() { Name = "Product Price", PropertyExpression = (entity => entity.Price) },
Operand = new Operand() { Type = OperandType.Multiplication },
Right = new ProductPriceProperty() { Name = "ExportFactor", PropertyExpression = (entity => config.Dictionary["ExportFactor"]) },

// only this property is needed, and it will handle reference to config object in the closure
Calculator = (entity) => entity.Price * config.Dictionary["ExportFactor"]
};

// Once calculation has been reconstruction from the persistence layer, it needs to be executed.
product.Price = calculation.Calculator(product);
}
}

在该示例中没有表达式树,只有通常的闭包方法。

更新1

左节点和右节点表达式的问题在于,每个表达式都链接到自己的 entity 参数,而不是我们创建的 ParameterExpression,它将指向真实的实体对象,因此我们需要用 ExpressionVisitor 将旧的重写为新的.用于解析和重写需求。

这是重写器的代码:

public class ParameterRewriter : ExpressionVisitor
{
private readonly ParameterExpression _expToRewrite;

public ParameterRewriter(ParameterExpression expToRewrite)
{
this._expToRewrite = expToRewrite;
}

protected override Expression VisitParameter(ParameterExpression node)
{
// we just use type checking to understand that it's our parameter, and we replace it with new one
if (node.Type == this._expToRewrite.Type) return this._expToRewrite;
return base.VisitParameter(node);
}
}

这是 CalculationPair 类:

public class CalculationPair<TEntityType, TProperty>
where TEntityType : class
where TProperty : struct
{
public ValueTypeProperty<TEntityType, TProperty> Left { get; set; }
public Operand Operand { get; set; }
public ValueTypeProperty<TEntityType, TProperty> Right { get; set; }

public TResult Calculate<TResult>(TEntityType entity)
where TResult : struct
{
TResult result = default(TResult);

var prop = Expression.Parameter(typeof(TEntityType), "param");
var visitor = new ParameterRewriter(prop);
var leftExp = visitor.Visit(Left.PropertyExpression.Body);
var rightExp = visitor.Visit(Right.PropertyExpression.Body);

Expression body;

switch (this.Operand.Type)
{
case OperandType.Multiplication:
body = Expression.Multiply(leftExp, rightExp);
break;
case OperandType.Addition:
body = Expression.Add(leftExp, rightExp);
break;
case OperandType.Division:
body = Expression.Divide(leftExp, rightExp);
break;
case OperandType.Sunbtraction:
body = Expression.Subtract(leftExp, rightExp);
break;
default:
throw new Exception("Unknown operand type");
}

var lambda = Expression.Lambda<Func<TEntityType, TResult>>(body, prop);

// compilation is long operation, so you might need to store this Func as property and don't compile it each time
var func = lambda.Compile();
result = func(entity);

return (result);
}
}

用法是一样的

product.Price = calculation.Calculate<float>(product);

关于c# - 创建可在运行时计算的表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20829367/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com