C# никогда сам не преобразует несвязанные типы. Строка, содержащая цифры, остаётся строкой, а double не превращается незаметно в int. Вы преобразуете явно, а инструмент зависит от того, что именно преобразуется: текст в число (разбор), один числовой тип в другой (приведение) или объект в более конкретный тип (as, is).
Строку в int: Parse, TryParse и Convert
Есть три стандартных способа превратить текст в int:
Вывод:
2500
TryParse ok: 1250
TryParse failed, d = 0
42
-17
int.TryParse возвращает bool и записывает результат в переменную out, которая при неудаче получает 0. Поскольку плохой ввод нормален для всего, что набирает пользователь, TryParse это выбор по умолчанию для ввода, файлов и строк запроса. Те же методы есть у каждого числового типа: long.TryParse, double.TryParse, decimal.Parse и так далее.
Что каждый из них делает с плохим вводом
Три метода различаются только тем, как они сообщают о неудаче:
Вывод:
int.Parse(null) throws ArgumentNullException
Convert.ToInt32(null) = 0
int.Parse("abc") throws FormatException
int.Parse("12.5") throws FormatException
int.Parse("1,000") throws FormatException
int.Parse("9999999999") throws OverflowException
int.Parse("") throws FormatException
FormatException несёт знакомое сообщение «Input string was not in a correct format» (в .NET 8 и новее «The input string '12.5' was not in a correct format» с цитатой текста) и это одна из самых частых ошибок времени выполнения в C#: "12.5" это не int, и "1,000" тоже. Чтобы принимать такие строки, разбирайте их как decimal или укажите парсеру, какие стили допустимы:
Вывод:
1000
255
13
10
Культура: почему «1.5» может стать 15
По умолчанию разбор использует текущую культуру, а культуры расходятся в разделителях. В немецкой и ряде других европейских культур десятичный разделитель это запятая, а точка разделяет тысячи. Одна и та же строка даёт разные числа:
Вывод:
1.5
15
1.5
Код, который читает файлы данных, JSON, конфигурацию или что-то сгенерированное машиной, всегда должен передавать CultureInfo.InvariantCulture и при разборе, и при форматировании. Иначе одна и та же программа работает на машине разработчика и неправильно читает каждое число на сервере с другим языком. Используйте текущую культуру только для текста, который набрал или будет читать человек.
Неявные и явные числовые преобразования
Расширяющие преобразования выполняются неявно: int в long, int в double, float в double, int в decimal. Преобразование, которое может потерять величину значения или его дробную часть, требует явного приведения (type)value. Одно исключение стоит помнить: int или long во float и long в double выполняются неявно, хотя могут терять точность: float f = 16_777_217; компилируется и сохраняет 16777216.
Вывод:
9 -9
10
2
4
705032704
9.99
7 7.0 7
Три вещи нужно запомнить. Приведение числа с плавающей точкой к целому отбрасывает дробную часть, а не округляет. Convert.ToInt32(double) округляет по банковскому правилу (половины идут к чётному соседу). А целочисленное приведение, в которое значение не помещается, молча переходит через границу, если не выполняется в контексте checked, где выбрасывается OverflowException (см. типы данных).
int x = 3.7; это ошибка компиляции (CS0266: cannot implicitly convert type 'double' to 'int', an explicit conversion exists): так компилятор просит вас выбрать между приведением и методом округления.
Числа и другие типы в строку
У каждого типа есть ToString(), а числовые типы принимают строку формата. Интерполяция строк вызывает то же форматирование:
Вывод:
42
000042
1234.50
1,234.50
$1,234.50
FF
1010
True
Order 000042: 1234.50
Коды форматирования (D, F, N, C, X, P, E) и пользовательские шаблоны вроде "0.00" описаны в разделе интерполяция строк.
Символы и логические значения
Вывод:
7
55
7
7
A
True
False
1
False
(int)'7' равно 55, коду символа, а не 7. На этом спотыкаются все, кто суммирует цифры числа; вычтите '0' или используйте char.GetNumericValue. bool.Parse принимает «true» и «false» в любом регистре и больше ничего («yes» и «1» вызывают исключение).
Ссылочные преобразования: приведение, as и is
Для классов приведение проверяет фактический тип объекта во время выполнения. Восходящее приведение к базовому классу неявное. Нисходящее приведение к производному классу требует явного приведения, которое выбрасывает InvalidCastException, если объект другого типа:
Вывод:
4242
True
Card ending 4242, 50.00
InvalidCastException
5
cannot unbox int as long
Предпочитайте is с переменной, когда нужно ветвиться по типу: это одна проверка, она не может выбросить исключение, а новая переменная видна только там, где проверка прошла. Используйте обычное приведение, когда другой тип был бы ошибкой, которая должна громко проявиться. Случай с упакованным int ловит тех, кто читает числа из коллекций object или ячеек DataRow: сначала распакуйте в точный тип ((long)(int)boxed) или используйте Convert.ToInt64(boxed), который справляется с любым числовым типом.
Часто задаваемые вопросы
Как преобразовать строку в int в C#?
Используйте int.TryParse(text, out int number), когда текст приходит от пользователя, из файла или по сети: метод возвращает false вместо исключения, если текст не является корректным числом. Используйте int.Parse(text), когда некорректный текст это ошибка в программе и исключение будет правильной реакцией. Convert.ToInt32(text) ведёт себя как int.Parse, но возвращает 0 для null.
Чем int.Parse отличается от Convert.ToInt32?
Для строк они почти одинаковы: оба выбрасывают FormatException на тексте вроде «abc» и OverflowException на числах вне диапазона. Разница в null: int.Parse(null) выбрасывает ArgumentNullException, а Convert.ToInt32(null) возвращает 0. Convert.ToInt32 также принимает другие типы (double, bool, object) и округляет double до ближайшего чётного целого, а не отбрасывает дробную часть.
Что означает «Input string was not in a correct format»?
Это сообщение FormatException, которое выбрасывают int.Parse, double.Parse или Convert.ToInt32 (в .NET 8 и новее оно звучит как «The input string 'abc' was not in a correct format» и цитирует текст), когда текст не является числом в ожидаемом формате: буквы, пустая строка, десятичная точка при разборе int или разделитель тысяч. Перейдите на TryParse, чтобы обрабатывать плохой ввод без исключения, и проверьте, в какой культуре записано число.
Как привести double к int в C#?
Напишите явное приведение: int n = (int)price;. Приведение отбрасывает дробную часть в сторону нуля, поэтому (int)9.99 равно 9, а (int)-9.99 равно -9. Чтобы округлить, используйте (int)Math.Round(price) и учтите, что Math.Round по умолчанию округляет половины до ближайшего чётного числа; передайте MidpointRounding.AwayFromZero для школьного округления.
Чем as отличается от приведения в C#?
Приведение (Dog)animal выбрасывает InvalidCastException, если объект не является Dog. animal as Dog вместо этого возвращает null, поэтому за ним должна следовать проверка на null, и работает он только со ссылочными типами и типами, допускающими null. Современная альтернатива это шаблон is: if (animal is Dog dog) { ... } проверяет и преобразует за один шаг.