.NET Tutorial
Junior7 мин чтенияВопрос 2 из 17

Типы данных в C#: int, double, decimal, bool, char, var, const и readonly?

Встроенные типы

Псевдоним C# Тип .NET Размер Диапазон / точность Пример
bool System.Boolean 1 байт true / false bool isActive = true;
byte System.Byte 1 байт 0…255 byte b = 200;
short System.Int16 2 байта ±32 тыс. short s = 1000;
int System.Int32 4 байта ±2,1 млрд int count = 42;
long System.Int64 8 байт ±9,2·10¹⁸ long id = 9_000_000_000L;
float System.Single 4 байта ~7 значащих цифр float f = 1.5f;
double System.Double 8 байт ~15–16 значащих цифр double d = 3.14;
decimal System.Decimal 16 байт 28–29 цифр, десятичный decimal price = 19.99m;
char System.Char 2 байта Символ UTF-16 char c = 'A';
string System.String Ссылка Неизменяемая строка string name = "Ann";
object System.Object Ссылка Базовый тип для всех object o = 1;

Суффиксы литералов: L — long, f — float, m — decimal, u — unsigned. Разделитель разрядов _ для читаемости: 1_000_000.

double против decimal

Классическая ловушка
Console.WriteLine(0.1 + 0.2 == 0.3);        // False!
Console.WriteLine(0.1 + 0.2);               // 0.30000000000000004

Console.WriteLine(0.1m + 0.2m == 0.3m);     // True

0.1 в десятичной системе

В двоичной — бесконечная дробь:
0.000110011001100...

Округляется до 52 бит мантиссы

Хранится ≈ 0.1000000000000000055...

decimal хранит число
как целое × 10^-n

0.1 = 1 × 10^-1 — точно

0.1 в десятичной системе

В двоичной — бесконечная дробь:
0.000110011001100...

Округляется до 52 бит мантиссы

Хранится ≈ 0.1000000000000000055...

decimal хранит число
как целое × 10^-n

0.1 = 1 × 10^-1 — точно

Почему double неточен
double decimal
Основание 2 (двоичное) 10 (десятичное)
Точность для 0.1, 0.01 Приблизительная Точная
Скорость Быстро (аппаратно) В разы медленнее (программно)
Диапазон Огромный (10³⁰⁸) Меньше (7,9·10²⁸)
Когда Физика, графика, статистика, ML Деньги, бухгалтерия, проценты, налоги

Переполнение

C#
int max = int.MaxValue;          // 2 147 483 647
int overflow = max + 1;          // -2 147 483 648 — без ошибки!

checked
{
    int boom = max + 1;          // OverflowException
}
int safe = checked(max + 1);     // то же в выражении

long big = (long)max + 1;        // 2 147 483 648 — расширили тип ДО сложения
long wrong = max + 1;            // -2 147 483 648 — сложение в int, потом расширение

Проверку переполнения для всего проекта можно включить в .csproj: <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> (обычно для Debug).

var, const, readonly

C#
var count = 10;                 // int — тип выведен из правой части
var names = new List<string>(); // List<string>
// count = "text";              // ошибка компиляции: тип int фиксирован

const double Pi = 3.14159;      // константа времени компиляции
const string AppName = "Shop";

public class Order
{
    private readonly DateTime _createdAt;       // задаётся один раз
    public static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);   // нельзя const: не примитив

    public Order() => _createdAt = DateTime.UtcNow;   // вычисляется при выполнении
}
const readonly static readonly
Когда задаётся При компиляции В объявлении или конструкторе В объявлении или статическом конструкторе
Какие типы Примитивы, string, enum, null Любые Любые
Уровень Всегда статический На экземпляр На тип
Значение Подставляется в код вызывающих сборок Читается из поля Читается из поля

Когда использовать var: когда тип очевиден из правой части (var list = new List<int>()) или длинный. Когда нет: когда тип неочевиден (var x = GetData(); — что вернулось?).

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

Неявное: int → long, int → double
без потери данных

Явное (cast): (int)3.99 → 3
возможна потеря, отбрасывает дробь

Из строки: int.Parse('42')
FormatException при ошибке

int.TryParse(s, out var n)
bool без исключений — для ввода пользователя

Convert.ToInt32(x)
null → 0, округление банковское

Неявное: int → long, int → double
без потери данных

Явное (cast): (int)3.99 → 3
возможна потеря, отбрасывает дробь

Из строки: int.Parse('42')
FormatException при ошибке

int.TryParse(s, out var n)
bool без исключений — для ввода пользователя

Convert.ToInt32(x)
null → 0, округление банковское

Способы преобразования
C#
int i = 100;
long l = i;                    // неявно
double d = 3.99;
int t = (int)d;                // 3 — дробная часть отброшена, не округлена
int r = (int)Math.Round(d);    // 4

if (int.TryParse(input, out var age))   // безопасный разбор пользовательского ввода
    Console.WriteLine($"Возраст: {age}");
else
    Console.WriteLine("Введите число");

decimal price = decimal.Parse("19.99", CultureInfo.InvariantCulture);   // культура важна: в ru-RU разделитель — запятая

Значения по умолчанию и nullable

C#
int a = default;      // 0
bool b = default;     // false
string? s = default;  // null

int? maybe = null;    // Nullable<int>: значение или null (например, необязательное поле из БД)
int value = maybe ?? 0;            // 0, если null
if (maybe.HasValue) Console.WriteLine(maybe.Value);

Вопросы на засыпку

Чем string отличается от String?

Ничем: string — псевдоним (ключевое слово C#) для System.String. То же для int/Int32, bool/Boolean. По соглашению используют псевдонимы в коде, а имена типов .NET — при вызове статических методов допустимы оба варианта (string.IsNullOrEmpty, String.Join).

Какой тип выбрать для денег и почему?

decimal: он хранит десятичные дроби точно, а double — приближённо, что даёт ошибки округления в копейках при суммировании. Альтернатива — хранить сумму в минимальных единицах (тийины, центы) в long.

Что выведет (int)2.9 и (int)-2.9?

2 и -2: явное приведение отбрасывает дробную часть (округление к нулю). Для математического округления — Math.Round (по умолчанию банковское: 2.5 → 2, 3.5 → 4), Math.Floor, Math.Ceiling.

Почему var не делает C# динамическим языком?

var — только сокращение записи: компилятор выводит конкретный тип из инициализатора, и дальше переменная строго типизирована. Динамическая типизация в C# — это dynamic, где проверки переносятся на время выполнения.

Итог

Выбирайте int и long для целых, double для научных расчётов и decimal для денег; помните о молчаливом переполнении и неточности double. var — вывод типа, а не динамическая типизация. const — для настоящих констант времени компиляции, readonly — для значений, вычисляемых один раз при создании. Пользовательский ввод разбирайте через TryParse с учётом культуры.