简体   繁体   中英

Multiple equations logic review

Currently learning C# and can't figure out where my logic goes wrong for a calculator I'm making. The calculator has three lines within a textbox the top line is the number the user enters via buttons the second is the equation shown on the screen eg 12+23-4 and the last is the answer which appears when the user presses equals.

The logic of the calculator fails for the following:

Steps to reproduce

  1. 5+5+20 -> Press equal button = 30
  2. Press minus then enter 10, press equals = 20
  3. Press minus then enter 10, press equals = 30

Its on the third step when trying to subtracting again it for some reason adds.

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 WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        double totalOne = 0;
        double totalTwo = 0;
        string startingState;
        string calculation;
        bool plusButtonpressed = false;
        bool minusButtonpressed = false;
        bool multiplyuButtonpressed = false;
        bool divideButtonpressed = false;

        private void btnOne_Click(object sender, EventArgs e)
        {
            startingState += btnOne.Text;
            calculation += btnOne.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;        

        }

        private void btnTwo_Click(object sender, EventArgs e)
        {
            startingState += btnTwo.Text;
            calculation += btnTwo.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnThree_Click(object sender, EventArgs e)
        {
            startingState += btnThree.Text;
            calculation += btnThree.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnFour_Click(object sender, EventArgs e)
        {
            startingState += btnFour.Text;
            calculation += btnFour.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnFive_Click(object sender, EventArgs e)
        {
            startingState += btnFive.Text;
            calculation += btnFive.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnSix_Click(object sender, EventArgs e)
        {
            startingState += btnSix.Text;
            calculation += btnSix.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnSeven_Click(object sender, EventArgs e)
        {
            startingState += btnSeven.Text;
            calculation += btnSeven.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnEight_Click(object sender, EventArgs e)
        {
            startingState += btnEight.Text;
            calculation += btnEight.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnNine_Click(object sender, EventArgs e)
        {
            startingState += btnNine.Text;
            calculation += btnNine.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnZero_Click(object sender, EventArgs e)
        {
            startingState += btnZero.Text;
            calculation += btnZero.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void button12_Click(object sender, EventArgs e)
        {
            startingState += btnDecimal.Text;
            calculation += btnDecimal.Text;
            textDisplay.Clear();
            textDisplay.Text = startingState + "\r\n" + calculation;
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            textDisplay.Clear();
        }

        private void btnPlus_Click(object sender, EventArgs e)
        {
            totalOne = totalOne + double.Parse(startingState);
            startingState = "";
            calculation += "+";
            textDisplay.Clear();
            textDisplay.Text = "" + "\r\n" + calculation;
            plusButtonpressed = true;
            minusButtonpressed = false;
            multiplyuButtonpressed = false;
            divideButtonpressed = false;
        }

        private void btnEqual_Click(object sender, EventArgs e)
        {

            if (plusButtonpressed == true)
            {
                totalTwo = totalOne + double.Parse(startingState);
                textDisplay.Text = totalTwo.ToString();
                textDisplay.Text = "" + "\r\n" + calculation + "\r\n" + "_______________" + "\r\n" + totalTwo;
            }
            else if (minusButtonpressed == true)
            {
                totalTwo = totalOne - double.Parse(startingState);
                textDisplay.Text = totalTwo.ToString();
                textDisplay.Text = "" + "\r\n" + calculation + "\r\n" + "_______________" + "\r\n" + totalTwo;
            }
            else if (multiplyuButtonpressed == true)
            {
                totalTwo = totalOne * double.Parse(startingState);
                textDisplay.Text = totalTwo.ToString();
                textDisplay.Text = "" + "\r\n" + calculation + "\r\n" + "_______________" + "\r\n" + totalTwo;
            }
            else if (divideButtonpressed == true)
            {
                totalTwo = totalOne / double.Parse(startingState);
                textDisplay.Text = totalTwo.ToString();
                textDisplay.Text = "" + "\r\n" + calculation + "\r\n" + "_______________" + "\r\n" + totalTwo;
            }


        }

        private void btnMinus_Click(object sender, EventArgs e)
        {
            totalOne = totalOne + double.Parse(startingState);
            startingState = "";
            calculation += "-";
            textDisplay.Clear();
            textDisplay.Text = "" + "\r\n" + calculation;
            plusButtonpressed = false;
            minusButtonpressed = true;
            multiplyuButtonpressed = false;
            divideButtonpressed = false;
        }

        private void btnMultiply_Click(object sender, EventArgs e)
        {

            totalOne = totalOne + double.Parse(startingState);
            startingState = "";
            calculation += "*";
            textDisplay.Clear();
            textDisplay.Text = "" + "\r\n" + calculation;
            plusButtonpressed = false;
            minusButtonpressed = false;
            multiplyuButtonpressed = true;
            divideButtonpressed = false;
        }

        private void btnDivide_Click(object sender, EventArgs e)
        {
            totalOne = totalOne + double.Parse(startingState);
            startingState = "";
            calculation += "/";
            textDisplay.Clear();
            textDisplay.Text = "" + "\r\n" + calculation;
            plusButtonpressed = false;
            minusButtonpressed = false;
            multiplyuButtonpressed = false;
            divideButtonpressed = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void btnRoot_Click(object sender, EventArgs e)
        {
            totalOne = double.Parse(textDisplay.Text);
            totalTwo = Math.Sqrt(totalOne);
            textDisplay.Clear();
            textDisplay.Text = totalTwo.ToString();
            totalOne = 0;

        }

        private void btnSin_Click(object sender, EventArgs e)
        {
            totalOne = double.Parse(textDisplay.Text);
            totalTwo = Math.Sin(totalOne);
            textDisplay.Clear();
            textDisplay.Text = totalTwo.ToString();
            totalOne = 0;
        }

        private void btnTan_Click(object sender, EventArgs e)
        {
            totalOne = double.Parse(textDisplay.Text);
            totalTwo = Math.Tan(totalOne);
            textDisplay.Clear();
            textDisplay.Text = totalTwo.ToString();
            totalOne = 0;
        }

        private void btnCos_Click(object sender, EventArgs e)
        {
            totalOne = double.Parse(textDisplay.Text);
            totalTwo = Math.Cos(totalOne);
            textDisplay.Clear();
            textDisplay.Text = totalTwo.ToString();
            totalOne = 0;
        }
    }
}

The problem is that you're not keeping track of your state properly.

After executing = the result you get needs to be put back in to totalOne for it to be ready to accept another operation.

Also, you're doing a lot of repetition. It's really hard to find bugs with that much code.

Try this instead:

public partial class Form1 : Form
{
    double totalOne = 0.0;
    string startingState;
    string calculation;
    Func<double, double, double> operation = null;

    public Form1()
    {
        InitializeComponent();

        btnClear.Click += (s, e) =>
        {
            startingState = "";
            totalOne = 0.0;
            calculation = "";
            operation = null;
            textDisplay.Clear();
        };

        var numerals = new[]
        {
            btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, btnEight, btnNine, btnDecimal,
        };

        foreach (var numeral in numerals)
        {
            var button = numeral;
            button.Click += (s, e) =>
            {
                startingState += button.Text;
                calculation += button.Text;
                textDisplay.Text = startingState + "\r\n" + calculation;
            };
        }

        var ops = new Dictionary<Button, Func<double, double, double>>()
        {
            { btnPlus, (x, y) => x + y },
            { btnMinus, (x, y) => x - y },
            { btnMultiply, (x, y) => x * y },
            { btnDivide, (x, y) => x / y },
        };

        foreach (var x in ops)
        {
            var button = x.Key;
            var op = x.Value;
            button.Click += (s, e) =>
            {
                var totalTwo = double.Parse(startingState);
                totalOne = operation == null ? totalTwo : operation(totalOne, totalTwo);
                startingState = "";
                calculation += button.Text;
                textDisplay.Text = "" + "\r\n" + calculation;
                operation = op;
            };
        }

        btnEqual.Click += (s, e) =>
        {
            if (operation != null)
            {
                var totalTwo = double.Parse(startingState);
                totalOne = operation(totalOne, totalTwo);
                textDisplay.Text = String.Join(Environment.NewLine, new[] { "", calculation, "_______________", totalOne.ToString() });
                startingState = totalOne.ToString();
                operation = null;
            }
        };
    }
}

Now, with this code, I can do this:

  1. 5+5+20 -> Press equal button = 30
  2. Press minus then enter 10, press equals = 20
  3. Press minus then enter 10, press equals = 10

Here's an approach - it's a little bit difficult to describe without understanding exactly what your code is doing, but the principle might be helpful.

Right now the state of your calculator exists entirely within your form - the values in the controls and a few variables. This would become easier to manage if you defined a class to contain all of those operations.

At any given point your calculator might be in one of a few states. If you're typing your first number and then you select an operator (plus, divided by, etc.) then your state changes. Now you're no longer entering the first number. The next thing entered is your second number. While you were typing your first number, an operator was an acceptable input. But if you've just selected an operator then the next thing you enter can't be another operator. Regardless of what state your calculator is in, you can always press "clear" and reset to a beginning state.

All of that sounds like a lot, but it's even more complicated if you're trying to build all of that logic into a form and trying to keep track of the state between all of these controls being updated.

If you write it as a class then you can focus on getting all of that logic straight without the extra complication of updating form controls and handling inputs. Instead of interacting with the calculator through form controls, you interact with it by setting and reading properties and executing methods.

You can also write unit tests. Instead of opening up your calculator form and testing every scenario manually, you can write a test that starts with a Calculator class, performs some operations, and then checks to make sure that it's in the state you expect and shows the results you expect.

Again, that sounds like a lot, but in order to get your calculator working you're going to have to do all of that anyway. You're going to end up testing it over and over and over again. Writing unit tests seems like more work, but ultimately it speeds things up and helps you work out your bugs much faster.

Once you've got your calculator class working, then you can create an instance of that class in your form. When users interact with the controls, the form calls methods and properties on the class. After the operation is done, the form updates to reflect the state of the calculator (like showing what the answer is.)

Another effect of this is that your calculator is abstract - it's not tied to one implementation, like a form. Now that you've done all the work of getting its logic right and you've written unit tests for it, you can use the same class in a console app or a web page.

The reason I emphasize this is because many programmers (including myself) work for years and years without ever learning to write unit tests or keep logic separate from presentation (like writing the calculator in one class instead of making it part of the form.) All of the stuff I described above sounds like more work. But once you learn the ideas and practice them, it's less work. You get code working faster and spend a lot less time debugging forms, pages, or other code looking for errors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM