Мой первый калькулятор C # [duplicate]

Это моя первая созданная мной программа. Это не очень хорошо, но я очень горжусь этим.

Если у вас есть предложения, которые я мог бы добавить в калькулятор, я с радостью это сделаю.

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

namespace CSharp_Shell
{
    public static class Program 
    {
        public static void Main() 
        {
           try
           {
                Console.WriteLine("--------------------------------");
                Console.WriteLine("Operators:");
                Console.WriteLine("+ = Add");
                Console.WriteLine("- = Subtract");
                Console.WriteLine("* = Multiply");
                Console.WriteLine("/ = Divide");
                Console.WriteLine("^ = Power");
                Console.WriteLine("--------------------------------");
                
                Console.Write("Enter operator: ");
                
                string op = (Console.ReadLine());
           
                Console.Write("Enter First Number: ");
                double num1 = Convert.ToDouble(Console.ReadLine());
          
                Console.Write("Enter Second Number: ");
                double num2 = Convert.ToDouble(Console.ReadLine());
                Console.WriteLine("--------------------------------");
           
           
                if (op == "+")
                {
                   Console.WriteLine(num1 + num2);
                   Console.WriteLine("--------------------------------");
                }
                else if (op == "-")
                {
                   Console.WriteLine(num1 - num2);
                   Console.WriteLine("--------------------------------");
                }
                else if (op == "/")
                {
                   Console.WriteLine(num1 / num2);
                   Console.WriteLine("--------------------------------");
                }
                else if (op == "*")
                {
                   Console.WriteLine(num1 * num2);
                   Console.WriteLine("--------------------------------");
                }
                else if (op == "^")
                {
                   double value1 = Math.Pow(num1, num2);  
                   Console.WriteLine("{0}", value1);
                   Console.WriteLine("--------------------------------");
                }
                else
                {
                   Console.ForegroundColor = ConsoleColor.Red;
                   Console.WriteLine("error");
                   
                   Console.ForegroundColor = ConsoleColor.White;
                   Console.WriteLine("--------------------------------");
                }
           }
           catch
           {
            Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("--------------------------------");
                
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("error");
            
            Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("--------------------------------");
           }
           Console.ForegroundColor = ConsoleColor.Gray;
           Console.Write("Press any key to stop the program");
           Console.ReadKey();
        }
    }
}

2 ответа
2

В целом, если вы пишете код впервые, вы действительно проделали звездную работу. Мне трудно поверить, что это была первая попытка без репликации чужого кода только из-за того, насколько четко вы подошли к нему. Очень хорошо сделано.

Приведенные ниже предложения больше дальнейшие улучшения чем они исправляют то, что вы делали. Некоторые из вещей, которые я предлагаю, могут не иметь полного смысла сейчас, но это полезные привычки, которые нужно развивать на раннем этапе, потому что, когда вы начнете работать над более сложными приложениями, вы значительно выиграете от того, что вам не придется изучать каждую ошибку на собственном опыте.


Избегайте повторения

Как часто вы видите этот код:

Console.ForegroundColor = /* some color */;
Console.WriteLine(/* some message */);

Ответ много. Итак, чтобы сэкономить повторяющийся код, преобразуйте его в метод:

public void WriteMessage(string message, ConsoleColor color = ConsoleColor.White)
{
    Console.ForegroundColor = color;
    Console.WriteLine(message);
}

Это кажется мелочью, но он фактически вдвое сокращает количество строк (вращающихся вокруг логики печати) в вашем основном методе, поскольку теперь вы можете просто сделать:

WriteMessage("error", ConsoleColor.Red);

Обратите внимание, что я также сделал цвет необязательным параметром, поэтому его можно не указывать, если особый цвет не требуется:

WriteMessage("a message without special color");

Улучшения синтаксиса

Это всего лишь мелочь, но обратите внимание, как каждое условие заканчивается рисованием линии:

if (op == "+")
{
    Console.WriteLine(num1 + num2);
    Console.WriteLine("--------------------------------");
}
else if (op == "-")
{
    Console.WriteLine(num1 - num2);
    Console.WriteLine("--------------------------------");
}
// and so on...

Если все они заканчиваются одной и той же инструкцией, проще просто поместить эту инструкцию за if / else:

if (op == "+")
{
    Console.WriteLine(num1 + num2);
}
else if (op == "-")
{
    Console.WriteLine(num1 - num2);
}
// and so on...        

Console.WriteLine("--------------------------------");     

Но можно пойти дальше. То же самое и с печатью результата. Таким образом, вы также можете переместить это за пределы цепочки:

double result = 0;

if (op == "+")
{
    result = num1 + num2;
}
else if (op == "-")
{
    result = num1 - num2;
}
// and so on...        

Console.WriteLine(result);   
Console.WriteLine("--------------------------------");   

Улучшения синтаксиса

Вам следует избегать if else цепочки, в которых вы продолжаете проверять, соответствует ли одна и та же переменная заданному значению. Это именно то, что switch был сделан для:

switch(op)
{
    case "+":
        Console.WriteLine(num1 + num2);
        break;
    case "-":
        Console.WriteLine(num1 - num2);
        break;
    case "*":
        Console.WriteLine(num1 * num2);
        break;
    // and so on...
}    

Разбить его

Код, который вы написали, относительно хорош для начинающей программы. Хороший отступ, разумное разделение пробелов между строками.

Но следующий шаг — разбить ваш метод на более мелкие шаги. Если бы я суммировал, что делает этот код, я бы перечислил его следующим образом:

  • Принимает номер (дважды)
  • Принимает математическую операцию
  • Пишет сообщения в консоль, иногда цветные.
  • Выполняет выбранную операцию над выбранными числами

Вы можете добавить сюда намного больше маркеров, если хотите, например, рассматриваете ли вы рисование горизонтального разделителя как задачу, отличную от написания сообщения на консоль. Я придерживаюсь этих четырех, поскольку это базовый пример.

Эти четыре пункта являются отличным (но не полным) руководством к тому, как вам нужно разбить приложение. Все, что вы считаете отдельной задачей, обычно должно быть отдельным методом. Итак, в этом случае вы будете искать такие методы, как:

  • double GetNumber()
  • char GetOperation() — Я бы подумал об использовании здесь пользовательского перечисления вместо символа, но пока давайте не будем усложнять.
  • void WriteMessage(string message, ConsoleColor color = ConsoleColor.White) — см. предыдущий пункт раздела
  • Раздельные методы оператора:
    • double Add(double a, double b)
    • double Subtract(double a, double b)
    • double Multiply(double a, double b)
    • double Divide(double a, double b)
    • double Power(double a, double b) — Это зеркала Math.Pow и вы можете технически пропустить свой собственный метод и использовать Math.Pow прямо. Однако из соображений абстракции рекомендуется по-прежнему определять свой собственный метод. Возможно, это выходит за рамки вашего текущего уровня навыков, но идея заключается в том, что, если вы предполагаете, что в будущем вы измените логику вычислений, будет легче переписать содержимое вашего Power метод один раз, чем искать все ссылки на Math.Pow и замените их все по отдельности.

Определенно еще есть возможности для улучшения, но вы все еще новичок, и это хороший первый шаг. Попробуйте написать код в своем Main метод таким образом, что он полагается на эти методы, которые я только что перечислил, вместо того, чтобы иметь Main меню делает всю тяжелую работу само по себе.

Подумайте об этом так: когда вы перестаете быть поваром и вместо этого становитесь поваром (= лучший кодекс), тогда вы больше не выполняете мелкую работу самостоятельно, например, чистите картошку и мыть посуду. Задачи делегируются. Повара ( Main метод) полагаются на мелочи, которые делают другие (подметоды).

Рефакторинг

Я заметил это:

Console.WriteLine("{0}", value1);

Это излишне сложно. Просто делать:

Console.WriteLine(value1);

Подозреваю, что это остатки рефакторинга. Раньше он делал больше, чем просто печатал value1, но со временем вы изменили код и никогда не уклонялись от использования String.Format подход.

Это совершенно нормально, но именно поэтому разработчики всегда должны возвращаться к коду, когда он «завершен», чтобы увидеть, есть ли какие-то остатки прошлых попыток, которые можно очистить или реорганизовать во что-то лучшее.

Лучший пользовательский опыт

Иногда полезно думать как пользователь, а не как разработчик. Как мы пишем, складывая 1 и 2 вместе?

  • + 1 2
  • 1 + 2
  • 1 2 +

Надеюсь, вы согласитесь, что второй пункт гораздо предпочтительнее. Обратите внимание, что также существует первая точка маркера (она называется Польская нотация), но если у вас нет причин полагать, что ваши пользователи преимущественно используют польскую нотацию, второй пункт маркера — это то, как пользователи думают.

Но когда вы запрашиваете ввод пользователя, вы просите + 1 2:

Console.Write("Enter operator: ");
string op = (Console.ReadLine());
       
Console.Write("Enter First Number: ");
double num1 = Convert.ToDouble(Console.ReadLine());
      
Console.Write("Enter Second Number: ");
double num2 = Convert.ToDouble(Console.ReadLine()); 

Это не соответствует тому, как думают пользователи. Это очень простое улучшение — просто изменить порядок вашего кода. Это не меняет принцип работы вашего кода, но улучшает взаимодействие с пользователем.

Еще лучший пользовательский интерфейс

Это не так просто, но в качестве следующей задачи вы могли бы увидеть, можете ли вы уйти от пользователей, которые должны вводить отдельные значения и нажимать ввод между ними.

Посмотрите, сможете ли вы выяснить, как разрешить пользователям вводить всю формулу, например "1+2" и возьми оттуда. Или, в качестве альтернативы, прочитайте каждую клавишу, которую нажимает пользователь, и динамически выясните, что они означают.

Если вы думаете о реальном калькуляторе, он работает без необходимости нажимать клавишу ввода между вводом данных. Так что попробуйте сделать свою похожую. Это большое улучшение пользовательского опыта и хорошее упражнение для тех, кто плохо знаком с программированием.

  • 1

    Большое спасибо! Это очень помогло, я постараюсь улучшить пользовательский ввод и пользовательский интерфейс и, надеюсь, сделать код более простым для чтения. Я боролся с Силой, поэтому я нашел ее пример, но все остальное сделано мной. Я посмотрел четырехчасовое видео о том, как программировать, но я думаю, что способ узнать больше — просто больше заниматься программированием и улучшать код. Еще раз большое вам спасибо за это.

    — Лук

  • @Sipuli говорит, что спасибо недостаточно на Stack Exchange и не является обязательным, даже если вы благодарный человек. Лучший способ сказать спасибо — отметить ответ как принятый. Флажок находится слева от текста ответа.

    — эспот

Разделение работы

Задача вашего калькулятора — принимать ввод, обрабатывать его и показывать результат пользователю. Если вы разделите эти 3 шага на отдельные функции, вы добавите ясности человеку, который читает ваш код. Более того, вы можете повторно использовать эти функции, если хотите снова выполнить тот же шаг.

Что-то вроде

var numbers  = getNumbersInput();
var op = getOperatorInput();

var result = calcResult(op, numbers.Item1, number2.Item2);
Console.WriteLine($"The result is {result}."); 

Здесь очевидна работа getNumbersInput() состоит в том, чтобы получить два числа и вернуть их. getOperatorInput() вернет оператора, который пользователь хочет использовать, и calcResult() будет использовать ввод, обрабатывать его и возвращать вам результат.


Не оборачивайте всю свою программу в try/catch

Есть только несколько областей, в которых ваша программа может столкнуться с исключением. Например, точка, в которой вы принимаете целочисленный ввод от пользователя. При неверном вводе вы можете использовать catch чтобы указать конкретные ошибки, чтобы помочь пользователю понять, что он сделал не так. Всего 2 белые строки со словом error не говорит вам, что случилось, не так ли?

Вместо этого используйте try/catch в определенных областях с соответствующими сообщениями об ошибках, такими как Invalid number input: Please enter a number.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *