У меня есть код консольного калькулятора, написан на C#, сделан в целях изучения. Не давно перешел на этот язык с Java, кружечка с кофе мне не подходила под интересы, программирую 2 года, но знаете, как-то все странно получалось, я скорее не старался понять язык, а изучил основы и дальше все на ходу доучивал, если кратко занимался разработкой чисто для релакса (хз как это, но это именно так) и неважно. В связи с тем, что я решил перейти на другой язык, мне показалось интересным начать именно изучать язык и понимать, как и что работает, а не писать говнокод. Я прошу у вас, программистов со стажем, посмотреть мой код и указать на ошибки или не логические действия, может где-то какую-то строку нужно убрать и заменить другой и т.п. В общем прошу, если не составит труда, направить меня на мои ошибки. Вот код:
namespace ConsoleApp1
{
class Program
{
private static int A;
private static int B;
static void Main(string[] args)
{
StartProgramm();
Close();
}
private static void StartProgramm()//полагает начало программы
{
MathGetNumber();
MathOperation();
}
private static void MathGetNumber()//получаем число
{
string AT;
string BT;
Console.WriteLine("Первое число");
AT = Console.ReadLine();
Console.WriteLine("Второе число");
BT = Console.ReadLine();
try
{
A = Convert.ToInt32(AT);
B = Convert.ToInt32(BT);
}
catch (FormatException)
{
Console.WriteLine("Не шали, вводи только числа!");
StartProgramm();
}
for (byte i = 0; i < 4; i++)//это я решил сделать отступ в несколько строк
{
Console.WriteLine();
}
}
private static void MathOperation()//производим операцию с числом
{
byte Operate;
string Operation;
if (A == 0 && B == 0)
{
Console.WriteLine("Числа имеют значение 0, продолжать работу программы нет смысла, завершение");
}
else
{
const byte Longe = 4;
byte OperNum = 1;
char[] NumUs = new char[Longe] { '+', '-', '*', ':' };
Console.WriteLine("Числа имеют значение > или < 0, выберите желаемую операцию над числом");
for (byte i = 0; i < Longe; i++)
{
Console.WriteLine(OperNum + ") " + NumUs[i]);
OperNum++;
}
Operation = Console.ReadLine();
try
{
Operate = Convert.ToByte(Operation);
if (Operate <= 4)
{
switch (Operate)
{
case 1:
Console.WriteLine("Ваше число = " + (A + B));
break;
case 2:
Console.WriteLine("Ваше число = " + (A - B));
break;
case 3:
Console.WriteLine("Ваше число = " + (A * B));
break;
case 4:
Console.WriteLine("Ваше число = " + (A / B));
break;
}
}
else
{
Console.WriteLine("Хватит шалить, вводи число от 1 до 4 включительно");
MathOperation();
}
}
catch (FormatException)
{
Console.WriteLine("Хватит шалить, вводи число от 1 до 4 включительно");
MathOperation();
}
}
}
private static void ReturnProgramm()//повторение работы программы с самого начала, на случай если пользователь захочет еще раз посчитать число
{
StartProgramm();
Close();
}
}
private static void Close()//завершение работы программы
{
string answer;
Console.WriteLine("Close program? y/n");
answer = Console.ReadLine();
if (answer == "y")
{
return;
}
else if (answer == "n")
{
ReturnProgramm();
}
else
{
Close();
}
}
}
}
Вообще каждый программист находит для себя свой стиль. Важно лишь соблюдать базовые принципы, заложенные той или иной технологией.
Мой ответ будет основан исключительно на собственном опыте и может не совпадать с мнением других.
Первое, что сразу бросилось в глаза - это методы без параметров и возвращаемых значений. Да, есть методы, которые могут быть без параметров и не возвращать ничего (собственно, ваши методы StartProgram()
и Close()
). Но вот лично мне хотелось бы, чтобы метод MathGetNumber()
возвращал значение, а не присваивал его какой-то глобальной переменной. Желательно, чтобы метод получал необходимые значения, проводил с ними определенные манипуляции и в случае необходимости возвращал значение. Если хотите все же оставить метод таким, то тогда лучше было бы его назвать, например, так: MathReadAB()
. Старайтесь, чтобы ваш метод делал ровно то, о чем говорит его название.
Второе: в C# в классах private-поля принято именовать с маленькой буквы:
private static int a;
а еще некоторые программисты к private-полям добавляют приставку с нижним подчеркиванием (это уже дело вкуса; при стандартных настройках Visual Studio подсказками предлагает именно такой вариант именования):
private static int _a;
Для C#, как и для любого другого ЯП, есть соглашение по именованию членов, переменных, функций... и соглашение по написанию кода.
Третье: не пытайтесь сэкономить на памяти, объявляя вместо int
переменную типа byte
. Это, может быть, и хорошо, однако излишняя оптимизация часто не оправдана. В данном случае, мне кажется, она не оправдана. К тому же, если вы проявите невнимательность, то тип byte
может переполниться без вашего ведома, что может привести к непредсказуемым результатам работы программы. В мировой практике для итераторов цикла и любых других счетчиков принято брать тип int
.
Четвертое: В методе MathOperation()
можно избавиться от лишних фигурных скобок, заменив
if (A == 0 && B == 0)
{
Console.WriteLine("Числа имеют значение 0, продолжать работу программы нет смысла, завершение");
}
else
{
//else-code
}
на
if (A == 0 && B == 0)
{
Console.WriteLine("Числа имеют значение 0, продолжать работу программы нет смысла, завершение");
return;
}
//else-code
Это довольно частая и хорошая практика, иначе если у вас в коде будет много проверок, будет много вложенных else
и фигурных скобок, что ухудшает читаемость кода.
Пятое: Это чисто логическая ошибка. Если у вас A=1
и B=0
, то программа продолжит свою работу, что приведет к DivideByZeroException
. Нужно переделать условие if (B == 0)
. Этого будет достаточно.
Шестое: Вместо конструкции
if (Operate <= 4)
{
switch (Operate)
{
case 1:
Console.WriteLine("Ваше число = " + (A + B));
break;
case 2:
Console.WriteLine("Ваше число = " + (A - B));
break;
case 3:
Console.WriteLine("Ваше число = " + (A* B));
break;
case 4:
Console.WriteLine("Ваше число = " + (A / B));
break;
}
}
else
{
Console.WriteLine("Хватит шалить, вводи число от 1 до 4 включительно");
MathOperation();
}
лучше воспользоваться default:
switch (Operate)
{
case 1:
Console.WriteLine("Ваше число = " + (A + B));
break;
case 2:
Console.WriteLine("Ваше число = " + (A - B));
break;
case 3:
Console.WriteLine("Ваше число = " + (A* B));
break;
case 4:
Console.WriteLine("Ваше число = " + (A / B));
break;
default:
Console.WriteLine("Хватит шалить, вводи число от 1 до 4 включительно");
MathOperation();
break;
}
В остальном все хорошо. Только Ваш стиль с объявлением переменных в начале метода - для меня непривычен, но он имеет место быть. Постарался осветить все проблемные места, что увидел.
В дополнение:
A = Convert.ToInt32(AT);
B = Convert.ToInt32(BT);
При использовании методов класса Convert единственный способ обработки неверного ввода - исключения. Генерация исключений является несколько тяжелой операцией, поэтому лучше использовать метод TryParse.
Console.WriteLine("Числа имеют значение 0, продолжать работу программы нет смысла, завершение");
Такая проверка не имеет смысла, арифметические операции с нолями вполне допустимы (кроме деления). 0+0 должно выдавать 0, а не ошибку.
char[] NumUs = new char[Longe] { '+', '-', '*', ':' };
//...
for (byte i = 0; i < Longe; i++)
Если массив зашит в коде, нет смысла также зашивать в коде его константную длину. Длину массива можно определить программно методом Length, так что при добавлении элементов не придется менять код в двух местах
Console.WriteLine("Хватит шалить, вводи число от 1 до 4 включительно");
Вы предлагаете пользователю ввести число, а затем преобразуете его в символ. Почему бы сразу не вводить символ? Это намного проще и удобнее.
Console.WriteLine("Close program? y/n");
answer = Console.ReadLine();
Так как вы просите пользователя ввести один из символов, оба из которых требуют одиночного нажатия клавиши, можно сделать его ввод чуть более удобным с использованием Console.ReadKey.
private static void StartProgramm()//полагает начало программы
{
MathGetNumber();
MathOperation();
}
Этот код привносит в программу "повторную входимость": StartProgramm вызывает MathGetNumber, который, в свою очередь, вызывает StartProgramm. Это приводит к тому, что стек вызовов в ходе работы программы будет постепенно расти. Для реализации повторяемости лучше использовать циклы, а не повторно входимые методы.
Мой вариант улучшения с учетом этих замечаний:
using System;
using System.Text;
namespace ConsoleApp1
{
class Program
{
private static int A;
private static int B;
static void Main(string[] args)
{
bool success;
while (true)
{
while (true)
{
success = MathGetNumber();
if (success) break;
}
while (true)
{
success = MathOperation();
if (success) break;
}
if (Close()) break;
}
}
private static bool MathGetNumber()//получаем число
{
string AT;
string BT;
Console.WriteLine("Первое число");
AT = Console.ReadLine();
Console.WriteLine("Второе число");
BT = Console.ReadLine();
if (Int32.TryParse(AT, out A) == false || Int32.TryParse(BT, out B) == false)
{
Console.WriteLine("Не шали, вводи только числа!");
return false;
}
for (byte i = 0; i < 4; i++)//это я решил сделать отступ в несколько строк
{
Console.WriteLine();
}
return true;
}
private static bool MathOperation()//производим операцию с числом
{
char[] NumUs = new char[] { '+', '-', '*', ':' };
Console.WriteLine("Введите один из знаков операций: ");
for (byte i = 0; i < NumUs.Length; i++)
{
Console.Write(NumUs[i]+" ");
}
Console.WriteLine();
string sign = Console.ReadLine();
switch (sign)
{
case "+":
Console.WriteLine("Ваше число = " + (A + B));
break;
case "-":
Console.WriteLine("Ваше число = " + (A - B));
break;
case "*":
Console.WriteLine("Ваше число = " + (A * B));
break;
case ":":
if (B == 0) Console.WriteLine("Делить на ноль нельзя");
else Console.WriteLine("Ваше число = " + (A / B));
break;
default:
Console.WriteLine("Не шали, введи один из допустимых знаков операции!");
return false;
}
return true;
}
private static bool Close()//завершение работы программы
{
char answer;
Console.WriteLine("Close program? y/n");
while (true)
{
answer = Console.ReadKey(true).KeyChar;
if (answer == 'y')
{
return true;
}
else if (answer == 'n')
{
return false;
}
}
}
}
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Вероятно, вопрос будет выглядеть глупо для вас, но я так и не смог найти ответ в православном гуглеКороче, у меня есть программка WPF, я для удобства...
Возможно ли добавить в консольное приложение кнопку? Чтобы пользователь не вводил сообщение сам, а нажимал на слово и получал результатСвоеобразный...
Имеется БД MySQL, которую надо импортировать в heroku, но heroku работает только с postgresОднако есть плагин ClearDB MySQL