Данные приходят в программу в одном виде, а нужны в другом: консоль отдаёт строки, а считать надо числа. Способов перевести одно в другое несколько, и выбор между ними – не вкусовщина.
Неявное преобразование: когда ничего не теряется
Если значение гарантированно помещается в новый тип, компилятор переводит его молча:
int whole = 42;
double fractional = whole; // никаких скобок не нужно
long big = whole;
Цепочка безопасных расширений выглядит так: int → long → float → double. Любое целое
влезет в double, любой int – в long, поэтому подтверждения не требуется.
Явное преобразование: когда что-то теряется
Обратный путь сужает диапазон, и компилятор требует написать это своей рукой:
double price = 3.99;
int rounded = price; // ошибка компиляции
int truncated = (int)price; // 3 – дробная часть отброшена
Скобки с именем типа называются приведением (cast). Это подпись под утверждением: «я знаю, что теряю данные, и меня это устраивает».
Приведение к целому отбрасывает дробную часть, а не округляет: (int)3.99 – это 3, а (int)(-3.99) – это -3. Если нужно округление, сначала Math.Round, и только потом приведение.
Сужение целых типов ведёт себя ещё резче – лишние биты просто отбрасываются:
int large = 300;
byte small = (byte)large; // 44, потому что байт хранит только 0 – 255
Никакого предупреждения при этом не будет: вы попросили – вам сделали.
Строка в число
Консоль, файлы и сеть отдают текст. Есть три способа превратить его в число, и различаются они поведением при плохом вводе.
int.Parse – самый прямой. Не смог разобрать – бросил исключение:
int age = int.Parse("19"); // 19
int oops = int.Parse("много"); // FormatException, программа падает
int.TryParse – аккуратный. Возвращает true или false, а результат кладёт в переменную:
string input = Console.ReadLine();
if (int.TryParse(input, out int number))
{
Console.WriteLine($"Вы ввели {number}");
}
else
{
Console.WriteLine("Это не число");
}
Конструкция out int number объявляет переменную прямо в аргументе – после вызова она доступна в
блоке. Если разобрать не удалось, в ней останется ноль, но полагаться на это не нужно: смысл
имеет только результат проверки.
Convert.ToInt32 – более широкий: принимает не только строки, но и другие типы, а null
превращает в ноль вместо исключения.
Правило простое: данные от пользователя, из файла или из сети разбирайте через TryParse. Parse оставьте для строк, в происхождении которых вы уверены – например, для констант в коде.
Как это устроено в других языках
Тот же разбор ввода с защитой от мусора:
if (int.TryParse(input, out int number))
{
Console.WriteLine(number * 2);
}
if input.isdigit():
print(int(input) * 2)
В Python преобразование и проверка – два отдельных действия, и между ними легко забыть связь. В C# они слиты в один вызов: результат разбора нельзя получить, не проверив успех. Разница не в удобстве, а в том, что забыть проверку сложнее.
Число в строку
Обратная дорога проще, потому что потерять здесь нечего:
int count = 42;
string a = count.ToString();
string b = $"{count}";
string c = "Всего: " + count;
Все три способа дают одно и то же. В реальном коде почти всегда пишут интерполяцию – она читается лучше остальных.
У ToString есть форма с форматом, та же, что и в интерполяции:
double price = 1234.5;
Console.WriteLine(price.ToString("F2")); // 1234,50
Console.WriteLine(price.ToString("C")); // валюта по настройкам системы
Разделитель дробной части и локаль
Тонкость, на которой ломаются программы при переезде на чужой сервер: разбор числа зависит от региональных настроек.
double x = double.Parse("3.14"); // на русской локали – FormatException
В русской локали разделителем считается запятая, и точку Parse не понимает. Для данных, которые
пришли из файла или по сети, нужно указать нейтральную культуру явно:
double x = double.Parse("3.14", CultureInfo.InvariantCulture);
Правило: текст для человека форматируется по локали пользователя, текст для машины (файлы, JSON, запросы к API) – всегда через InvariantCulture. Смешаете – получите отчёт, который читается только на компьютере автора.
Классу CultureInfo нужен using System.Globalization;. В новых проектах с включённым
ImplicitUsings это пространство имён не подключено по умолчанию – строчку придётся дописать.