Мой калькулятор не работает, когда первый операнд равен нулю

Рейтинг: 2Ответов: 1Опубликовано: 31.07.2023

Я новичок в C#. Решил попробовать Windows Forms (до этого работал только с консолью). По официальной документации сделал макет приложения, логику же решил написать сам, пытаясь повторить поведение стандартного калькулятора Windows.

Работает всё следующим образом:

  • _operation хранит значение (enum Operation) последней используемой операции;
  • _operatorActive принимает true если есть незавершённая операция (например: пользователь нажал '+', но ещё не нажал '=');
  • _inputActive: если значение false, то при вводе числа панель вывода (textBox1) сначала очищается (чтобы ввести новое число);
  • Если пользователь, после выбора операции не введёт второй операнд (_secondOperand), а сразу нажмет '=', то второй операнд будет равен первому (_firstOperand);
  • Если пользователь, после выбора операции и ввода второго операнда, выберет ещё одну операцию (вместо '='), то предыдущая операция выполнится автоматически, а её результат станет первым операндом для текущей операции;
  • Если пользователь будет несколько раз подряд нажимать на '=', то будет выполняться последняя сохранённая операция с последним сохранённым вторым операндом.

Всё работает как задумано, НО если первый операнд равен нулю (_firstOperand = 0), то при выборе операции сложения, результат операции не присваивается первому операнду. Однако если первый операнд равен любому другому числу – всё работает корректно.

С операцией вычитания и умножения всё также работает корректно (деление пока не реализовано).

Также всё работает правильно и в следующем случае:

  1. Мы сначала выполняем какую-либо операцию с другим значением первого операнда (например: 3 + 5, и нажимаем '=');
  2. После – устанавливаем первому операнду значение 0 (просто нажимаем на '0');
  3. Повторно нажимаем '=';
  4. В этом случае, как и ожидалось, выполнится предыдущая операция, но с другим первым операндом;

Код:

namespace Calculator;

public partial class Form1 : Form
{
    private int _result, _firstOperand, _secondOperand;
    private Operation? _operation = null;
    private bool _operatorActive = false;
    private bool _inputActive = false;

    public Form1()
    {
        InitializeComponent();
        textBox1.Text = _result.ToString();

        UpdateDebugValues();
    }

    private void NumButton_Click(object sender, EventArgs e)
    {
        if (textBox1.MaxLength > textBox1.Text.Length)
        {
            if (!_inputActive)
            {
                textBox1.Clear();
                _inputActive = true;
            }
            textBox1.Text += (sender as Button)!.Text;
        }

        UpdateDebugValues();
    }
    private void ClearButton_Click(object sender, EventArgs e)
    {
        _operatorActive = false;
        _inputActive = false;
        _result = 0;
        _operation = null;
        _secondOperand = 0;
        textBox1.Clear();
        textBox1.Text = _result.ToString();

        UpdateDebugValues();
    }
    private void OperButton_Click(object sender, EventArgs e)
    {
        if (_operatorActive)
        {
            EqualsButton_Click(sender, e);
        }
        _operatorActive = true;
        _operation = (Operation?)(sender as Button)!.Tag;
        _secondOperand = _firstOperand;
        _inputActive = false;

        UpdateDebugValues();
    }
    private void EqualsButton_Click(object sender, EventArgs e)
    {
        if (_operation == null)
        {
            _result = _firstOperand;
        }
        else
        {
            _result = Expression.Calculate(_firstOperand, _secondOperand, _operation);
        }
        _operatorActive = false;
        textBox1.Text = _result.ToString();
        _inputActive = false;

        UpdateDebugValues();
    }

    private void TextBox1_TextChanged(object sender, EventArgs e)
    {
        if (int.TryParse(textBox1.Text, out int value))
        {
            if (_operatorActive)
            {
                _secondOperand = value;
            }
            else
            {
                _firstOperand = value;
            }
        }
    }

    private void UpdateDebugValues()
    {
        firstText.Text = _firstOperand.ToString();
        operText.Text = _operation?.ToString() ?? "NULL";
        operActText.Text = _operatorActive.ToString();
        secondText.Text = _secondOperand.ToString();
        resultText.Text = _result.ToString();
    }
}

Дополнительно:

  • Под вводом числа и выбором операции подразумевается нажатие соответствующих кнопок;
  • UpdateDebugValues() отображает значения переменных в окне приложения. На логику программы не влияет;
  • Expression.Calculate(int, int, Operation?) возвращает результат операции:
internal static class Expression
{
    public static int Calculate(int leftOperand, int rightOperand, Operation? operationType)
    {
        return operationType switch
        {
            Operation.Add => leftOperand + rightOperand,
            Operation.Subtract => leftOperand - rightOperand,
            Operation.Multiply => leftOperand * rightOperand,
            Operation.Divide => leftOperand / rightOperand,
            _ => throw new ArgumentException("Unknown operation."),
        };
    }
}

P.S. Это мой первый вопрос на StackOverflow. Обычно все ответы на мои вопросы уже где-нибудь подробно расписаны, но этот я даже не придумал как кратко описать :)

Ответы

▲ 2Принят

Скорее всего, событие TextBox1_TextChanged не выстреливает если текст не меняется. Например, пишем 0 + 3, в тексте остается "3" и результат также будет "3".

В коде этот сегмент:

private void EqualsButton_Click(object sender, EventArgs e)
{
  if (_operation == null)
  {
    _result = _firstOperand;
  }
  else
  {
    _result = Expression.Calculate(_firstOperand, _secondOperand, _operation);
  }
  _operatorActive = false;
  textBox1.Text = _result.ToString();
  // Здесь нужен способ обновить первый операнд
  _inputActive = false;

  UpdateDebugValues();
}

private void TextBox1_TextChanged(object sender, EventArgs e)
{
  if (int.TryParse(textBox1.Text, out int value))
  {
    if (_operatorActive)
    {
      _secondOperand = value;
    }
    else
    {
      _firstOperand = value;
    }
  }
}

Изменить на:

private void EqualsButton_Click(object sender, EventArgs e)
{
  if (_operation == null)
  {
    _result = _firstOperand;
  }
  else
  {
    _result = Expression.Calculate(_firstOperand, _secondOperand, _operation);
  }
  _operatorActive = false;
  textBox1.Text = _result.ToString();
  if (_secondOperand == _result)
    SetOperand(_result);
  _inputActive = false;

  UpdateDebugValues();
}

private void TextBox1_TextChanged(object sender, EventArgs e)
{
  if (int.TryParse(textBox1.Text, out int value))
  {
    SetOperand(value);
  }
}

private void SetOperand(int value)
{
  if (_operatorActive)
  {
    _secondOperand = value;
  }
  else
  {
    _firstOperand = value;
  }
}