Программа курса← C#: Базовый курс
1Знакомство с C# и .NET
2Данные и типы
3Ветвления и циклы
4Методы и ошибки
5Коллекции и строки
6Классы и объекты

Преобразования типов

Данные приходят в программу в одном виде, а нужны в другом: консоль отдаёт строки, а считать надо числа. Способов перевести одно в другое несколько, и выбор между ними – не вкусовщина.

Неявное преобразование: когда ничего не теряется

Если значение гарантированно помещается в новый тип, компилятор переводит его молча:

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 это пространство имён не подключено по умолчанию – строчку придётся дописать.