首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用ExpressionVisitor创建算法公式

使用ExpressionVisitor创建算法公式
EN

Stack Overflow用户
提问于 2018-03-03 06:59:46
回答 1查看 400关注 0票数 0

我试图从模型的列中通过实体框架lambda创建一个动态公式。

代码语言:javascript
复制
public class OutputModel
{
   public decimal Result {get;set;}
}

public class TableTest
{
  public decimal A {get;set;}
  public decimal B {get;set;}
  public decimal C {get;set;}
}

Expression<Func<TableTest, OutputModel>> _expr = t => new OutputModel();

TestExpressionVisitor _visitor = new TestExpressionVisitor();
_visitor.Visit(_expr);

var _result = new TempDataContext().TableTests.Select(_expr);

我正在考虑使用表达式访问者来修改结果。

代码语言:javascript
复制
public class TestExpressionVisitor : ExpressionVisitor
{
    public override Expression Visit(Expression node)
    {
        return base.Visit(node);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        return base.VisitMember(node);
    }
}

但不太确定如何构造一个表达式,该表达式可以从字符串参数({ A }+{B}({C}))中对列执行算术函数,其中A、B和C来自TableTest,并将结果放在TableTest上。我是否在正确的道路上使用表达式访问者?

任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-03-03 12:23:01

为了详细说明我的意见,一般来说,你需要:

  1. 解析输入并将其转换为令牌,其中令牌只是转换为C#对象的字符串的语义组件。 如果使用示例(A+B)*C,则标记将是对象“括号(打开)、变量(A)、运算符(+)、变量B、括号(关闭)、运算符(*)、变量(C)”。 我不会详细讨论如何做到这一点,因为这不是问题的一部分,但我曾经用正则表达式序列编写了一个非常简单但功能强大的标记器,因为它们已经完成了解析所需的大部分艰苦工作。
  2. 重新排序您的令牌,就像它们需要被处理一样,考虑操作符优先规则和括号。这可以通过使用Dijkstra的调车码算法来完成,参见algorithm中的一个例子。 您重新排序的令牌现在是“变量(A)、变量(B)、运算符(+)、变量(C)、变量(*)”。
  3. 将令牌树或队列(无论您如何选择存储它)转换为表达式树。

如果出现令牌队列,最后一步是:

代码语言:javascript
复制
  // for demo purposes I manually fill the list of tokens
  // with the tokens in order how they are output by the shunting-yard algorithm
  var tokenQueue = new Token[]
  {
    new VariableToken("A"),
    new VariableToken("B"),
    new OperatorToken("+"),
    new VariableToken("C"),
    new OperatorToken("*")
  };
  var inputParameter = Expression.Parameter(typeof(TableTest));
  var expressions = new Stack<Expression>();
  foreach (var token in tokenQueue)
  {
    // transform token to expression by using the helper methods of https://msdn.microsoft.com/de-de/library/system.linq.expressions.expression_methods(v=vs.110).aspx
    switch (token)
    {
      case VariableToken variableToken:
        // this will reference the property in your TableTest input specified by the variable name, e.g. "A" will reference TableTest.A
        expressions.Push(Expression.Property(inputParameter, variableToken.Name));
        break;
      case OperatorToken operatorToken:
        // This will take two expression from the stack, give these to input to an operator and put the result back onto the queue for use for the next operator
        var rightOperand = expressions.Pop();
        var leftOperand = expressions.Pop();
        if (operatorToken.Name == "+")
        {
          expressions.Push(Expression.Add(leftOperand, rightOperand));
        }
        else if (operatorToken.Name == "*")
        {
          expressions.Push(Expression.Multiply(leftOperand, rightOperand));
        }
        break;
    }
  }

  // create and fill output model with final expression
  var outputModelExpr = Expression.New(typeof(OutputModel).GetConstructor(new[] {typeof(decimal) }), expressions.Single());

  // create the lambda expression 
  // in this example it will have the form: x => return new OutputModel((x.A + x.B) * x.C)
  Expression<Func<TableTest, OutputModel>> lambda = Expression.Lambda<Func<TableTest, OutputModel>>(outputModelExpr, inputParameter);

  // only for testing purposes: compile it to a function and run it
  var calc = lambda.Compile();
  var testInput = new TableTest { A = 1, B = 2, C = 3 };
  Console.WriteLine(calc(testInput).Result); // returns 9, because (A + B) * C = (1 + 2) * 3 = 9

使用令牌类:

代码语言:javascript
复制
  public abstract class Token
  {
    public string Name { get; protected set; }
  }

  public class VariableToken : Token
  {
    public VariableToken(string name) { Name = name; }
  }

  public class OperatorToken : Token
  {
    public OperatorToken(string name) { Name = name; }
  }

请注意,我向OutputModel添加了一个构造函数,因为这使得表达式更加容易:

代码语言:javascript
复制
public class OutputModel
{
   public OutputModel(decimal result) { Result = result; }

   public decimal Result {get;set;}
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49081604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档