利用System.Linq.Expressions实现四则运算计算器(三)
将表达式树转换成.NET定义的表达式树
请大家留意上次提供的类图:
抽象类BWExpression提供了一个抽象方法GetExpression,这个方法就是用于获取表达式类的。
对于常量表达式BWConstantExpressionNode节点,GetExpression只需把数字作为常量表达式返回:
///<summary> ///生成表达式 ///</summary> ///<returns>表达式</returns> public override Expression GetExpression() { return Expression.Constant(this.Operand, typeof(double)); } |
对于BWExpressionNodeCollection节点,GetExpression需要对其表达式子节点针对运算符优先级进行组合。
///<summary> ///生成表达式 ///</summary> ///<returns>表达式</returns> public override Expression GetExpression() { // 1 + 5 * 9 - ( 1 + 2 ) * 3 / 4 + 6 * ( -2 ) // 将加、减放入队列中,乘、除计算后也放入队列中 List<ExpressionWriper> expressionList = new List<ExpressionWriper>(); expressionList.Add(new ExpressionWriper(this.expressionNodeList[0].GetExpression(), this.expressionNodeList[0].ExpressionNodeType)); for (int i = 1; i < this.expressionNodeList.Count; i++) { BWExpressionNode currentNode = this.expressionNodeList[i]; switch (currentNode.ExpressionNodeType) { case BWExpressionNodeType.Addition: case BWExpressionNodeType.Subtration: expressionList.Add(new ExpressionWriper(currentNode.GetExpression(), currentNode.ExpressionNodeType)); break; case BWExpressionNodeType.Multiplication: { ExpressionWriper preNode = expressionList[expressionList.Count - 1]; expressionList[expressionList.Count - 1] = new ExpressionWriper(Expression.Multiply(preNode.Expression, currentNode.GetExpression()), preNode.ExpressionNodeType); break; } case BWExpressionNodeType.Division: { ExpressionWriper preNode = expressionList[expressionList.Count - 1]; expressionList[expressionList.Count - 1] = new ExpressionWriper(Expression.Divide(preNode.Expression, currentNode.GetExpression()), preNode.ExpressionNodeType); break; } } } Expression expression = null; // 对队列中的加、减进行计算 for (int i = 0; i < expressionList.Count; i++) { ExpressionWriper wriper = expressionList[i]; switch (wriper.ExpressionNodeType) { case BWExpressionNodeType.Start: expression = wriper.Expression; break; case BWExpressionNodeType.Addition: expression = Expression.Add(expression, wriper.Expression); break; case BWExpressionNodeType.Subtration: expression = Expression.Subtract(expression, wriper.Expression); break; case BWExpressionNodeType.Multiplication: expression = Expression.Multiply(expression, wriper.Expression); break; case BWExpressionNodeType.Division: expression = Expression.Divide(expression, wriper.Expression); break; } } return expression; } |
在这个方法中,我引入一个辅助类ExpressionWriper,用于保存临时表达式和其前面的符号。
private class ExpressionWriper { public BWExpressionNodeType ExpressionNodeType { get; set; } public Expression Expression { get; set; } public ExpressionWriper(Expression expression, BWExpressionNodeType type) { this.Expression = expression; this.ExpressionNodeType = type; } } |
这个GetExpression方法中,使用一个列表来临时存储加减表达式。由于乘法和除法需要优先计算,在方法中,先是把第一个表达式放入队列,再判断第二个表达式的符号,如果是加号或者减号,则直接把第二个表达式放入队列,如果遇到的是乘号或除号,则把队列的最后一个拿出来与当前表达式进行计算,结果放回队列的最后一个。这样,就保持了队列中只有加减表达式,没有乘除,因为乘法和除法都计算过了。看下面的流程图。
这个方法最后得到一个包含所有节点和子节点的表达式,返回给调用者。
为了方便使用,我写了一个静态方法,从传入字符串到得到计算结果,一次完成!
public class BWExpression { Func<double> func; public Func<double> Func { get { return func; } set { func = value; } } public void CreateFunc(string input) { // 1 + 5 * 9 - ( 1 + 2 ) * 3 / 4 + 6 * ( -2 ) BWExpressionNodeCollection nodes = new BWExpressionNodeCollection(input, BWExpressionNodeType.Start); Expression expression = nodes.GetExpression(); Expression<Func<double>> lamdbaExpression = Expression.Lambda<Func<double>>(expression); func = lamdbaExpression.Compile(); } public static double Calculate(string expressionString) { BWExpression expression = new BWExpression(); expression.CreateFunc(expressionString); return expression.Func(); } } |
CreateFunc(string input)方法在《初识System.Linq.Expressions》中提到过,大家可以连接过去看看。
最后建一个窗体:
按钮的事件处理如下:
private void btnCalc_Click(object sender, EventArgs e) { try { txtOutput.Text = BWExpression.Calculate(txtInput.Text).ToString(); } catch { MessageBox.Show("表达式错误!"); } } |