Null и значимые типы в C#

В C# все типы делятся на значимые и ссылочные. Значимые типы int, bool, double, DateTime, структуры - хранятся в стеке и всегда содержат конкретное значение. По умолчанию int равен 0, bool - false, и присвоить им null нельзя. Ссылочные типы, напротив, могут быть null — «ссылка никуда не ведёт».

Проблема возникает при работе с базами данных, API и формами, где значения часто отсутствуют. Если в столбце Age нет данных, чтение его в int делает «нет значения» неотличимым от нуля. Для решения этой проблемы в C# существуют nullable-типы — обёртки над значимыми типами, добавляющие возможность хранить null.

 

Создание и устройство

Nullable-тип создаётся добавлением вопросительного знака к значимому типу:

int? age = null;

double? price = 99.95;

bool? isEnabled = null;

Под капотом int? — это System.Nullable<int> — структура с двумя полями: значением базового типа и флагом HasValue. Ограничение where T : struct означает, что обернуть можно только значимый тип, а Nullable<Nullable<int>> не существует.

 

Доступ к значению

Два свойства позволяют работать с nullable-переменной:

int? score = 42;

if (score.HasValue)

    Console.WriteLine(score.Value);  // 42

Обращение к Value при null выбрасывает InvalidOperationException. Безопасная альтернатива — GetValueOrDefault(), которая возвращает default(T) или заданное значение, не выбрасывая исключений:

int? empty = null;

int r1 = empty.GetValueOrDefault();     // 0

int r2 = empty.GetValueOrDefault(-1);   // -1

 

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

Базовый -> nullable выполняется неявно и без потерь:

int number = 10;

int? nullableNumber = number;  // неявно

Nullable -> базовый требует явного приведения и проверки на null:

int? nullableNumber = 42;

int number = (int)nullableNumber;  // OK

int? nullableNull = null;

int failed = (int)nullableNull;    // исключение!

Между самими nullable-типами работают те же правила, что и между их базовыми типами. Если исходная переменная равна null, результат тоже null.

Особый случай, это упаковка: nullable-переменная со значением null боксится в null, а со значением в свой базовый тип, что упрощает передачу в методы, ожидающие object.

 

Операции с nullable-типами

Арифметические операторы следуют правилу lifted operators: если хотя бы один операнд null, результат тоже null.

int? a = 10, b = null, c = 5;

int? sum1 = a + c;   // 15

int? sum2 = a + b;   // null

Операторы сравнения >, <, >=, <= возвращают false, если любой операнд null. Операторы == и != ведут себя иначе: null == null даёт true.

Тип bool? реализует трёхзначную логику:

a            b            a & b    a | b

true      null       null       true

false     null       false     null

null       null       null       null

 

Оператор null-объединения

Оператор ?? самый частый способ безопасно извлечь значение. Он возвращает левый операнд, если он не null, иначе правый

int? score = null;

int result = score ?? 0;         // 0

int value = TryGetFromCache() ?? TryGetFromDb() ?? 0;  // цепочка

Начиная с C# 8 оператор ??= присваивает значение только при null:

int? count = null;

count ??= 10;   // count теперь 10

 

Nullable и ссылочные типы

С C# 8 появилась аннотация string? для ссылочных типов. Важно различать: int? реальная структура Nullable<int>, а string? тот же string с пометкой для статического анализатора. Никакой обёртки во втором случае не создаётся.

 

Nullable-типы элегантный мост между миром значимых типов и реальностью, где данные не всегда есть. Они избавляют от магических чисел-заглушек вроде «-1 означает отсутствие» и делают код точнее отражающим предметную область.

Главное правило при работе с ними всегда осознанно обрабатывать случай null: через ??, HasValue или GetValueOrDefault. Пренебрежение этой проверкой самый частый источник InvalidOperationException и NullReferenceException в коде, использующем nullable-типы.

Logo

Spartacus_85 [Admin]

Администратор сайта — это специалист, который отвечает за техническую поддержку и бесперебойную работу веб-ресурса.



0 Комментарий(я)

Зарегистрируйтесь чтобы оставить комментарий