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

«Что такое Nullable-типы в C#?» — вопрос из категории C# Core, который задают на 25% собеседований 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.