簡體   English   中英

使用ExpressionVisitor創建算術公式

[英]Create Arithmetic Formula using ExpressionVisitor

我正在嘗試從模型的列通過實體框架lambda創建動態公式

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

我當時在考慮使用表達式訪問者來修改結果

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,並將結果放在OutputModel.Result 我在使用Expressionvisitor的正確路徑上嗎?

任何幫助,將不勝感激。

要擴展我的評論,通常需要:

  1. 解析您的輸入並將其轉換為令牌,其中令牌只是轉換為C#對象的字符串的語義成分。
    如果我們使用示例(A+B)*C則標記將是對象“括號(打開),變量(A),運算符(+),變量B,括號(關閉),運算符(*),變量(C )”。
    我不會詳細介紹如何執行此操作,因為這不是問題的一部分,但是我曾經使用正則表達式序列編寫了一個非常簡單但功能強大的令牌生成器,因為它們已經完成了解析過程中所需的大部分工作。
  2. 考慮操作符優先級規則和括號,對令牌進行重新排序以使其需要處理。 這可以通過使用Dijkstra的shunting-yard算法來完成,有關示例,請參見https://en.wikipedia.org/wiki/Shunting-yard_algorithm
    您重新排序的令牌現在為“變量(A),變量(B),運算符(+),變量(C),變量(*)。
  3. 將令牌樹或隊列(無論如何選擇存儲)轉換為表達式樹。

在令牌隊列的情況下,最后一步是:

  // 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

使用令牌類:

  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添加了一個構造函數,因為這使表達式更加容易:

public class OutputModel
{
   public OutputModel(decimal result) { Result = result; }

   public decimal Result {get;set;}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM