Что такое Nullable-типы в C#?

Ответ

Nullable-типы в C# — это структуры, которые позволяют переменным значимых типов (value types) представлять отсутствие значения, то есть иметь состояние null. Это решает проблему, когда int, bool, DateTime и другие структуры по умолчанию не могут быть null.

Синтаксис

  • Полная форма: Nullable<T> (например, Nullable<int>).
  • Сокращенная форма (синтаксический сахар): T? (например, int?, bool?, DateTime?).

Основные свойства и методы

int? nullableNumber = null;
DateTime? nullableDate = DateTime.Now;

// 1. Проверка наличия значения
if (nullableNumber.HasValue) // или if (nullableNumber != null)
{
    // 2. Получение значения (выбросит InvalidOperationException, если HasValue == false)
    int actualNumber = nullableNumber.Value;
    // Альтернатива: безопасное получение со значением по умолчанию
    int safeNumber = nullableNumber.GetValueOrDefault(); // вернет 0, если null
    int safeNumberWithDefault = nullableNumber.GetValueOrDefault(42); // вернет 42, если null
}

// 3. Приведение
int? a = 10;
int b = (int)a; // Явное приведение (опасно, если a == null)

// 4. Оператор объединения с null (null-coalescing operator)
int result = nullableNumber ?? 100; // Если nullableNumber != null, вернет его значение, иначе 100.
int result2 = nullableNumber ?? nullableDate?.Day ?? 0; // Цепочка операторов.

// 5. Условный null (null-conditional operator) с nullable
int? length = nullableString?.Length; // length будет int? (null если nullableString == null)

Практическое применение

  1. Работа с базами данных: Поля таблицы (например, EndDate, MiddleName) часто могут быть NULL.
  2. Опциональные параметры методов: Вместо использования "магических" значений (например, -1).
  3. Состояния флагов: bool? может означать true/false/неизвестно.

Пример с Entity Framework/Dapper:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string? MiddleName { get; set; } // Может быть null в БД
    public DateTime BirthDate { get; set; }
    public DateTime? LastLoginDate { get; set; } // Может быть null, если пользователь не логинился
}

Null Forgiving Operator (!): Используется, чтобы сообщить компилятору, что выражение не будет null, даже если его статический анализ предполагает обратное. Используйте с осторожностью.

int? maybeNumber = GetPossibleNumber();
int definiteNumber = maybeNumber!.Value; // Мы "прощаем" возможный null, рискуя получить исключение.

Важно: Nullable reference types (NRT) (string? для ссылочных типов) — это отдельная функция C# 8+, которая помогает избежать NullReferenceException, но работает на уровне анализа компилятора, а не в runtime.

Ответ 18+ 🔞

А, нулевые типы в C#! Это ж та самая штука, которая позволяет твоему int или DateTime внезапно стать никем, то есть null. Ну знаешь, как сотрудник, который числится в штате, но на самом деле его нихуя нет. Вот это оно.

Короче, раньше если ты объявлял int number, то там всегда была какая-то циферка, хоть ноль. А если тебе надо было показать, что значения нет — типа, пользователь ещё не логинился, и дата последнего входа неизвестна — то приходилось выкручиваться. Костыли ставить, вроде DateTime.MinValue или магического числа -1. Пиздец, а не жизнь.

Так вот, Nullable<T> (или просто T?) — это как раз легальный способ сказать: «Эй, эта переменная — структура, но она может и отсутствовать, нахуй».

Как этим пользоваться, не сломав себе всё

Синтаксис — проще пареной репы:

  • Длинно и официально: Nullable<int> — для пафоса.
  • Коротко и по делу: int? — так пишут все, кто не мудак.

Вот тебе основные приёмы, чтобы не обосраться:

int? maybeNumber = null; // Ага, пустота. Ничего.
DateTime? maybeDate = DateTime.Now; // А тут есть значение.

// 1. Первое правило: прежде чем лезть, проверь, не пусто ли там.
if (maybeNumber.HasValue) // Или if (maybeNumber != null) — кому как удобнее.
{
    // 2. Вот теперь можно вытаскивать значение. Осторожно, блядь!
    int actualNumber = maybeNumber.Value; // Если тут окажется null — получишь InvalidOperationException прямо в ебало.

    // 3. Безопасный способ для трусливых (и умных).
    int safeNumber = maybeNumber.GetValueOrDefault(); // Если null — вернёт 0.
    int safeNumberWithDefault = maybeNumber.GetValueOrDefault(42); // Если null — вернёт твою любимую 42.
}

// 4. Оператор объединения с null (??) — твой лучший друг.
// Читай так: «Возьми то, что слева, если оно не null. Если оно null — дай мне то, что справа, нахуй».
int result = maybeNumber ?? 100; // Если maybeNumber не пустой — result = ему. Если пустой — result = 100.
int result2 = maybeNumber ?? maybeDate?.Day ?? 0; // Можно строить цепочки, как домино.

// 5. Условный null (?.) — тоже крутая штука.
string? nullableString = null;
int? length = nullableString?.Length; // Если строка null, то и length будет null (int?). Никаких NullReferenceException!

Где это, блядь, пригождается в жизни?

  1. Базы данных. Там половина полей может быть NULL. Дата увольнения, отчество, рейтинг — да что угодно. Без nullable-типов ты бы с ума сошел, мапя это на обычные int.
  2. Методы с опциональными параметрами. Вместо того чтобы передавать -1 или DateTime.MinValue и потом гадать, что это значит, можно просто передать null. Честно и понятно.
  3. Три состояния вместо двух. bool? — это не просто «да» или «нет». Это «да», «нет» или «хз, иди нахуй». Полезно для опросов, согласий и состояния твоего мозга в понедельник утром.

Пример из реальности (типа Entity Framework):

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; } // Обычно не null.
    public string? MiddleName { get; set; } // А вот это запросто может быть null в базе. Отчества же нет у всех, ёпта.
    public DateTime BirthDate { get; set; } // Дата рождения обязательна.
    public DateTime? LastLoginDate { get; set; } // А вот дата последнего входа — может быть null, если чел только зарегался и ещё не заходил.
}

Про оператор «прощения» null (!)

Вот это, сука, опасная штука. Ты говоришь компилятору: «Отстань, я знаю, что тут не null, хоть ты и думаешь иначе». Это как снять предупредительную ленту «не влезай, убьёт» и сунуть руку в щиток.

int? maybeNumber = GetPossibleNumber(); // Допустим, этот метод может вернуть null.
int definiteNumber = maybeNumber!.Value; // Восклицательный знак — это твоё "верь мне, бро!".
// Если maybeNumber таки null — получишь исключение. Сам дурак.

Используй только если ты на 146% уверен. А то будет тебе InvalidOperationException вместо рабочего кода.

И на последок: не путай это с Nullable reference types (NRT) — это когда ты пишешь string? для ссылочных типов. Это другая фича, она на уровне компилятора работает, чтобы он тебя пинал за потенциальные NullReferenceException. А int? — это реальная структура Nullable<int>, которая живёт в рантайме. Две большие разницы, как говорят в Одессе.