Problem
I tried my best to replicate, in style and functionality, the Windows standard calculator:
I would like to get some feedback on the efficiency and readability of my code as well as the approach i chose (for example the one of using the evaluate function on the expression textbox):
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _2022_01_27
{
public partial class Form1 : Form
{
bool equals_last_pressed;
double last_number_entry = 0;
public Form1()
{
InitializeComponent();
}
private void Add_to_entry(string digit)
{
if (tbox_entry.Text.Length <= 18)
{
tbox_entry.Text += digit;
tbox_entry.Text = Convert.ToString(double.Parse(tbox_entry.Text));
}
equals_last_pressed = false;
}
static Double Evaluate(String expression)
{
System.Data.DataTable table = new System.Data.DataTable();
return Convert.ToDouble(table.Compute(expression, String.Empty));
}
private void btn_0_Click(object sender, EventArgs e)
{
Add_to_entry("0");
}
private void btn_1_Click(object sender, EventArgs e)
{
Add_to_entry("1");
}
private void btn_2_Click(object sender, EventArgs e)
{
Add_to_entry("2");
}
private void btn_3_Click(object sender, EventArgs e)
{
Add_to_entry("3");
}
private void btn_4_Click(object sender, EventArgs e)
{
Add_to_entry("4");
}
private void btn_5_Click(object sender, EventArgs e)
{
Add_to_entry("5");
}
private void btn_6_Click(object sender, EventArgs e)
{
Add_to_entry("6");
}
private void btn_7_Click(object sender, EventArgs e)
{
Add_to_entry("7");
}
private void btn_8_Click(object sender, EventArgs e)
{
Add_to_entry("8");
}
private void btn_9_Click(object sender, EventArgs e)
{
Add_to_entry("9");
}
private void btn_c_Click(object sender, EventArgs e)
{
tbox_entry.Text = "0";
tbox_expression.Text = "";
last_number_entry = 0;
}
private void btn_ce_Click(object sender, EventArgs e)
{
tbox_entry.Text = "0";
}
private void btn_backspace_Click(object sender, EventArgs e)
{
tbox_entry.Text = tbox_entry.Text.Remove(tbox_entry.Text.Length - 1);
if (tbox_entry.Text.Length == 0)
tbox_entry.Text = "0";
}
private void btn_addition_Click(object sender, EventArgs e)
{
last_number_entry = double.Parse(tbox_entry.Text);
if (equals_last_pressed == false)
tbox_expression.Text += tbox_entry.Text;
tbox_expression.Text = $"{Evaluate(tbox_expression.Text)} + ";
tbox_entry.Text = "0";
equals_last_pressed = false;
}
private void btn_subtraction_Click(object sender, EventArgs e)
{
last_number_entry = double.Parse(tbox_entry.Text);
if (equals_last_pressed == false)
tbox_expression.Text += tbox_entry.Text;
tbox_expression.Text = $"{Evaluate(tbox_expression.Text)} - ";
tbox_entry.Text = "0";
equals_last_pressed = false;
}
private void btn_multiplication_Click(object sender, EventArgs e)
{
last_number_entry = double.Parse(tbox_entry.Text);
if (equals_last_pressed == false)
tbox_expression.Text += tbox_entry.Text;
tbox_expression.Text = $"{Evaluate(tbox_expression.Text)} * ";
tbox_entry.Text = "0";
equals_last_pressed = false;
}
private void btn_division_Click(object sender, EventArgs e)
{
last_number_entry = double.Parse(tbox_entry.Text);
if (equals_last_pressed == false)
tbox_expression.Text += tbox_entry.Text;
tbox_expression.Text = $"{Evaluate(tbox_expression.Text)} / ";
tbox_entry.Text = "0";
equals_last_pressed = false;
}
private void btn_sqrtx_Click(object sender, EventArgs e)
{
tbox_entry.Text = $"{Math.Sqrt(double.Parse(tbox_entry.Text))}";
}
private void btn_x2_Click(object sender, EventArgs e)
{
tbox_entry.Text = $"{Math.Pow(double.Parse(tbox_entry.Text), 2)}";
}
private void btn_1dividex_Click(object sender, EventArgs e)
{
if (tbox_entry.Text == "0")
return;
tbox_entry.Text = $"{1 / double.Parse(tbox_entry.Text)}";
}
private void btn_equals_Click(object sender, EventArgs e)
{
last_number_entry = double.Parse(tbox_entry.Text);
if (equals_last_pressed == true)
return;
equals_last_pressed = true;
tbox_expression.Text += $"{tbox_entry.Text}";
tbox_entry.Text = Convert.ToString(Evaluate(tbox_expression.Text));
}
private void btn_period_Click(object sender, EventArgs e)
{
foreach(char digit in tbox_entry.Text)
if (digit == '.')
return;
tbox_entry.Text += ".";
}
private void btn_changesign_Click(object sender, EventArgs e)
{
if (tbox_entry.Text.Contains("-"))
tbox_entry.Text = tbox_entry.Text.Remove(0,1);
else
tbox_entry.Text = "-" + tbox_entry.Text;
}
private void btn_percentage_Click(object sender, EventArgs e)
{
tbox_entry.Text = Convert.ToString(last_number_entry / 100 * double.Parse(tbox_entry.Text));
}
}
}
Solution
Since aepot has already mentioned a way how can you reduce the code of the btn_{n}_Click
event handlers so, I will not address that problem here.
Add_to_entry
- I think the second
tbox_entry.Text
assignment is pointless - This piece of code
double.Parse(tbox_entry.Text)
is being used in several places I suggest to introduce a helper method for that
private double GetEntryAsNumber()
=> double.Parse(tbox_entry.Text);
Evaluate
- According to my understanding you don’t need to create a new
Table
each and every time when you call this method - So, you can convert the
table
to a static member of the class
static System.Data.DataTable table = new System.Data.DataTable();
static double Evaluate(string expression)
=> Convert.ToDouble(table.Compute(expression, string.Empty));
btn_c_Click
and btn_ce_Click
- This code
tbox_entry.Text = "0"
is being used in multiple places so you can define a helper method for this
private void SetEntryToZero()
=> tbox_entry.Text = "0";
private void btn_c_Click(object sender, EventArgs e)
{
SetEntryToZero();
tbox_expression.Text = "";
last_number_entry = 0;
}
private void btn_ce_Click(object sender, EventArgs e)
=> SetEntryToZero();
btn_addition_Click
… btn_division_Click
- As I can see their code are almost identical except that part which comes after the
Evaluate
call, so the common part can be extracted
private void btn_addition_Click(object sender, EventArgs e)
=> PrepareForTheSecondOperand("+");
private void btn_subtraction_Click(object sender, EventArgs e)
=> PrepareForTheSecondOperand("-");
private void btn_multiplication_Click(object sender, EventArgs e)
=> PrepareForTheSecondOperand("*");
private void btn_division_Click(object sender, EventArgs e)
=> PrepareForTheSecondOperand("/");
private void PrepareForTheSecondOperand(string operation)
{
last_number_entry = GetEntryAsNumber();
if (!equals_last_pressed)
tbox_expression.Text += tbox_entry.Text;
tbox_expression.Text = $"{Evaluate(tbox_expression.Text)} {operation} ";
SetEntryToZero();
equals_last_pressed = false;
}
- Instead of using the
== false
you can simply use the negation operator
btn_sqrtx_Click
… btn_1dividex_Click
- All three methods can be implemented as a one-liner
- In case of
1dividex
you can use the conditional operator
- In case of
private void btn_sqrtx_Click(object sender, EventArgs e)
=> tbox_entry.Text = $"{Math.Sqrt(GetEntryAsNumber())}";
private void btn_x2_Click(object sender, EventArgs e)
=> tbox_entry.Text = $"{Math.Pow(GetEntryAsNumber(), 2)}";
private void btn_1dividex_Click(object sender, EventArgs e)
=> tbox_entry.Text = tbox_entry.Text == "0" ? tbox_entry.Text : $"{1 / GetEntryAsNumber()}";
btn_equals_Click
- Two minor things:
- You don’t need to use string interpolation when you do the assignment for
tbox_expression.Text
- You don’t need to use
Convert.ToString
static method, you can simply call theToString
instance method on the double
- You don’t need to use string interpolation when you do the assignment for
private void btn_equals_Click(object sender, EventArgs e)
{
last_number_entry = GetEntryAsNumber();
if (equals_last_pressed)
return;
equals_last_pressed = !equals_last_pressed;
tbox_expression.Text += tbox_entry.Text;
tbox_entry.Text = Evaluate(tbox_expression.Text).ToString();
}
btn_period_Click
, btn_changesign_Click
and btn_percentage_Click
- All three methods can be implemented as a one-liner
- In case of
period
you can use Linq’sAny
to find a dot - In case of
changesign
you can use conditional operator
- In case of
private void btn_period_Click(object sender, EventArgs e)
=> tbox_entry.Text += tbox_entry.Text.ToCharArray().Any(c => c == '.') ? "" : ".";
private void btn_changesign_Click(object sender, EventArgs e)
=> tbox_entry.Text = tbox_entry.Text.Contains("-") ? tbox_entry.Text.Remove(0, 1): "-" + tbox_entry.Text;
private void btn_percentage_Click(object sender, EventArgs e)
=> tbox_entry.Text = Convert.ToString(last_number_entry / 100 * GetEntryAsNumber());