Introduction
C# is an interesting language,in my opinion,beginners always need some samples for learning,I hope my article can help them.As your know,the function of windows calculator is so strong even standard version,it can process expression like this:"2+==*.3-4+5===" or "2.5*3-.6+4.4=+==-=" and so on,I think make a calculator without any bug is hard,so a good design can help you to decrease your bug,so I will try to do this.
Using the code
CalculatorOperation
- a class to provide basic function of calculator.
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace NewCalculator
{
public enum Operator { NO_OP, ADD, SUB, MUL, DIV, EQUAL, SQRT, BACKWARDS,PERCENT }
internal class CalculatorOperation
{
public delegate decimal MathematicOperating(decimal opt1, decimal opt2);
public delegate decimal OtherOperating(decimal opt1);
public static Hashtable hashTable;
static CalculatorOperation()
{
hashTable = new Hashtable();
hashTable.Add("+", Operator.ADD);
hashTable.Add("-", Operator.SUB);
hashTable.Add("*", Operator.MUL);
hashTable.Add("/", Operator.DIV);
hashTable.Add("=", Operator.EQUAL);
}
private static decimal Add(decimal op1, decimal op2)
{
return decimal.Add(op1, op2);
}
private static decimal Sub(decimal op1, decimal op2)
{
return decimal.Subtract(op1, op2);
}
private static decimal Mul(decimal op1, decimal op2)
{
return decimal.Multiply(op1, op2);
}
private static decimal Div(decimal op1, decimal op2)
{
return decimal.Divide(op1, op2);
}
private static decimal Sqrt(decimal op1)
{
return (decimal)Math.Sqrt((double)op1);
}
private static decimal Backwords(decimal op1)
{
return 1 / op1;
}
private static decimal Percent(decimal op1)
{
return op1 / 100;
}
public static MathematicOperating Mathematic(Operator opt)
{
MathematicOperating MO = null;
if (opt == Operator.ADD)
{
MO = new MathematicOperating(CalculatorOperation.Add);
return MO;
}
if (opt == Operator.SUB)
{
MO = new MathematicOperating(CalculatorOperation.Sub);
return MO;
}
if (opt == Operator.MUL)
{
MO = new MathematicOperating(CalculatorOperation.Mul);
return MO;
}
if (opt == Operator.DIV)
{
MO = new MathematicOperating(CalculatorOperation.Div);
return MO;
}
return null;
}
public static OtherOperating OtherMathematic(Operator opt)
{
OtherOperating MO;
if (opt == Operator.SQRT)
{
MO = new OtherOperating(CalculatorOperation.Sqrt);
return MO;
}
if (opt == Operator.BACKWARDS)
{
MO = new OtherOperating(CalculatorOperation.Backwords);
return MO;
}
if (opt == Operator.PERCENT)
{
MO = new OtherOperating(CalculatorOperation.Percent);
return MO;
}
return null;
}
}
}
CalculatorController
- a class to simulate a calculator.
Blocks of code should be set as style "Formatted"
like this:
using System;
using System.Collections.Generic;
using System.Text;
namespace NewCalculator
{
public delegate void Change(string str);
public class CalculatorController:IController
{
#region private field
private decimal mStore = 0;
private decimal mResult = 0;
private string mDisplay = string.Empty;
private decimal opt1 = 0;
private decimal opt2 = 0;
private bool opt1IsReady = false;
private bool opt2IsReady = false;
private Operator currentOperator = Operator.NO_OP;
private bool isClickOperatingButton = false;
private bool isClickDigitalButton = false;
private bool isCalculating = false;
private Change onChange = null;
#endregion
#region public attribute
public string Display
{
get
{
return this.mDisplay;
}
set
{
this.mDisplay = value;
}
}
public Change OnChange
{
get
{
return this.onChange;
}
set
{
onChange = value;
}
}
#endregion
#region private method
private bool MathematicProcess(Operator curOpt)
{
if (currentOperator == Operator.NO_OP)
{
if (curOpt != Operator.EQUAL)
this.currentOperator = curOpt;
this.opt1IsReady = true;
this.opt2IsReady = false;
this.isCalculating = false;
return false;
}
else
{
if (Operator.EQUAL != curOpt)
{
if (this.opt1IsReady && this.opt2IsReady && !this.isCalculating)
{
this.mResult = CalculatorOperation.Mathematic(this.currentOperator)(this.opt1, this.opt2);
this.mDisplay = this.mResult.ToString();
opt1 = this.mResult;
this.opt1IsReady = true;
this.opt2IsReady = false;
this.isCalculating = false;
}
if (curOpt != this.currentOperator)
{
this.currentOperator = curOpt;
}
{
this.opt1 = decimal.Parse(this.mDisplay.ToString());
this.opt2IsReady = false;
this.isCalculating = false;
}
}
if (Operator.EQUAL == curOpt)
{
if (this.opt1IsReady && this.opt2IsReady)
{
this.mResult = CalculatorOperation.Mathematic(this.currentOperator)(this.opt1, this.opt2);
opt1 = this.mResult;
this.mDisplay = this.mResult.ToString();
this.opt1IsReady = true;
this.opt2IsReady = true;
}
if (this.opt1IsReady && !this.opt2IsReady)
{
this.mResult = decimal.Parse(this.mDisplay.ToString());
this.mResult = CalculatorOperation.Mathematic(this.currentOperator)(this.mResult, this.opt1);
this.mDisplay = this.mResult.ToString();
this.opt1IsReady = true;
this.opt2IsReady = false;
}
this.isCalculating = true;
}
return true;
}
}
#endregion
#region public method
public void InitializeCalculatorController()
{
this.mDisplay = string.Empty;
this.mResult = 0;
this.opt1 = 0;
this.opt2 = 0;
this.opt1IsReady = false;
this.opt2IsReady = false;
currentOperator = Operator.NO_OP;
isClickOperatingButton = true;
isCalculating = false;
}
public void FunctionButtonClick(string sSub)
{
if ("MC" == sSub.Trim().ToUpper())
{
this.mStore = 0;
if (OnChange != null)
OnChange("");
return;
}
if ("MR" == sSub.Trim().ToUpper())
{
this.isCalculating = false;
this.mDisplay = this.mStore.ToString();
return;
}
if ("MS" == sSub.Trim().ToUpper())
{
this.mStore = decimal.Parse(this.mDisplay);
if (OnChange != null)
OnChange("M");
return;
}
if ("M+" == sSub.Trim().ToUpper())
{
this.mStore += decimal.Parse(this.mDisplay);
if (OnChange != null)
OnChange("M");
return;
}
}
public void BackspaceClick()
{
if (!this.isCalculating)
{
if (this.mDisplay.Length > 1)
{
this.mDisplay = this.mDisplay.Substring(0, this.mDisplay.Length - 1);
}
else
{
this.mDisplay = "0";
}
}
}
public bool DigitalButtonClick(string sSub)
{
try
{
isClickDigitalButton = true;
if ("+/-" != sSub.Trim() && !(sSub.Equals("0") && sSub.Equals(this.mDisplay.Trim())))
{
if (!sSub.Equals("0") && this.mDisplay.Equals("0") && !sSub.Equals("."))
{
this.mDisplay = sSub;
return true;
}
if (sSub.Equals("."))
{
if (this.mDisplay.IndexOf(".") >= 0
{
if (this.isCalculating)
{
this.mDisplay = "0";
isClickOperatingButton = false;
return true;
}
return false;
}
if (this.isClickOperatingButton)
{
if (this.mDisplay.Equals("0") || this.mDisplay.Equals(""))
{
this.mDisplay = "0";
isClickOperatingButton = false;
return true;
}
}
}
if (!this.isClickOperatingButton)
{
this.mDisplay += sSub;
}
else
{
isClickOperatingButton = false;
this.mDisplay = sSub;
}
}
else
if ("+/-" == sSub.Trim())
{
decimal tmp = 0.0m - decimal.Parse(this.mDisplay);
if (tmp == 0)
{
if (this.mDisplay.IndexOf('-') >= 0)
this.mDisplay = this.mDisplay.Substring(1, this.mDisplay.Length-1);
else
this.mDisplay = "-" + tmp.ToString();
}
else
this.mDisplay = tmp.ToString();
}
return true;
}
catch
{
return false;
}
}
public bool OperatingButtonClick(string sSub)
{
try
{
if (!this.opt1IsReady)
{
this.opt1 = decimal.Parse(this.mDisplay);
this.opt1IsReady = true;
}
else
if (isClickDigitalButton && !this.opt2IsReady)
{
this.opt2IsReady = true;
this.opt2 = decimal.Parse(this.mDisplay);
}
isClickOperatingButton = true;
isClickDigitalButton = false;
MathematicProcess((Operator)CalculatorOperation.hashTable[sSub]);
return true;
}
catch
{
return false;
}
}
public bool ExecuteExpression(string expression, Change change)
{
bool flag = true;
foreach (char ch in expression)
{
if (ch >= '0' && ch <= '9' || '.' == ch)
{
flag = this.DigitalButtonClick(ch.ToString());
change(this.mDisplay);
continue;
}
if ('+' == ch || '-' == ch || '*' == ch || '/' == ch || '=' == ch)
{
flag = this.OperatingButtonClick(ch.ToString());
change(this.mDisplay);
continue;
}
}
return flag;
}
public decimal OtherCalculationg(string opt)
{
if ("sqrt" == opt.ToLower())
{
this.isClickOperatingButton = true;
return CalculatorOperation.OtherMathematic(Operator.SQRT)(decimal.Parse(this.mDisplay));
}
if ("1/x" == opt.ToLower())
{
this.isClickOperatingButton = true;
return CalculatorOperation.OtherMathematic(Operator.BACKWARDS)(decimal.Parse(this.mDisplay));
}
if ("%" == opt.ToLower())
{
this.isClickOperatingButton = true;
return CalculatorOperation.OtherMathematic(Operator.PERCENT)(decimal.Parse(this.mDisplay));
}
return 0.0m;
}
#endregion
}
}
History
08.01.2008 - Initial release