Безопасность null в C#

Подсказка

Эта статья является частью раздела "Основы" для разработчиков , которые уже знают хотя бы один язык программирования и учат C#. Если вы не знакомы с программированием, сначала начните с учебных пособий по началу работы.

Исходя из Java или C++? C# обеспечивает безопасность null во время компиляции с помощью ссылочных типов, допускающих значение NULL. Цель аналогична аннотациям Java @NonNull, но их обеспечение осуществляется компилятором. C# также имеет выделенные операторы ?. и ??, которые делают null-безопасные выражения краткими.

null представляет отсутствие значения. При попытке получить доступ к члену в null ссылке путем вызова метода или чтения свойства среда выполнения выдает NullReferenceException.

// Accessing a member on null throws NullReferenceException at runtime:
// string? name = null;
// int length = name.Length; // throws NullReferenceException

// Check before you dereference:
string? name = null;
if (name is not null)
{
    Console.WriteLine($"Name has {name.Length} characters.");
}
else
{
    Console.WriteLine("Name has no value.");
}
// Output: Name has no value.

C# предоставляет три дополнительных средства для написания кода, безопасного со значением NULL:

  • Типы значений допускающие значение null: позволяют типу значения, например int или bool, также хранить null.
  • Ссылочные типы, допускающие значение NULL, позволяют компилятору отслеживать, может ли ссылка быть null
  • Операторы NULL: безопасный для null доступ и логика запасного варианта

Типы значений, допускающие значение NULL

Такие типы значений, как int, doubleи bool не могут храниться null по умолчанию. Добавьте ? к имени типа, чтобы создать nullable тип данных, который может содержать либо значение, либо null:

int? score = null;
Console.WriteLine(score.HasValue);               // False

score = 95;
Console.WriteLine(score.HasValue);               // True
Console.WriteLine(score.GetValueOrDefault());    // 95

int? missing = null;
Console.WriteLine(missing.GetValueOrDefault(-1)); // -1

Типы значений, допускающие значение NULL, полезны, если базовый тип значения должен представлять "нет данных". Распространенные сценарии включают столбцы базы данных, которые могут быть отсутствуют, необязательные параметры конфигурации и данные датчиков, которые еще не записаны.

Полный охват объявления, проверки и преобразования см. в разделе "Типы значений, допускающих значение NULL".

Ссылочные типы, допускающие значение null

Ссылочные типы, такие как string массивы и экземпляры классов, могут содержать null во время выполнения. Ссылочные типы, допускающие значение NULL — это функция компилятора, которая делает намерение null явным и выявляет ошибки во время компиляции.

Используя аннотацию ?, вы объявляете свое намерение:

  • string? — это возможноnullссылка; компилятор предупреждает, если вы разыменовываете её без предварительной проверки.
  • string — эта ссылка не должна быть null; компилятор предупреждает, если ему присваивают null.
// string?  means this reference might be null
// string   means this reference should not be null
string? nullableName = null;
string  nonNullName  = "Alice";

// ?. safely accesses a member when the reference might be null
string display = nullableName?.ToUpper() ?? "(no name)";
Console.WriteLine(display);         // (no name)

display = nonNullName.ToUpper();    // safe: nonNullName is never null
Console.WriteLine(display);         // ALICE

Все проекты .NET, которые создают современные шаблоны SDK, по умолчанию включают ссылочные типы, допускающие значение NULL. Полные рекомендации по включению и аннотации см. в разделе "Ссылочные типы, допускающие значение NULL".

Операторы NULL

C# включает в себя несколько операторов, которые позволяют писать код, безопасный в случае null, без использования ручной проверки на null везде.

Оператор Name Purpose
?. Доступ к члену с условием NULL Доступ к члену только в том случае, если объект не является NULL
?[] Нулевой условный доступ к индексатору Доступ к элементу только в том случае, если коллекция не является NULL
?? Объединение с значением NULL Возвращает резервное значение, если выражение имеет значение null
??= Присваивание по объединяющему оператору NULL Назначайте только в том случае, если переменная null
is null / is not null Шаблон NULL Предпочтительный тест на null
string? city = GetCity();

// ?. — access a member only when non-null
int? len = city?.Length;

// ?? — substitute a default when null
string display = city ?? "unknown";

// is null — preferred null test
if (city is null)
{
    Console.WriteLine("No city provided.");
}
else
{
    Console.WriteLine($"{display} ({len} chars)");
}
// Output: No city provided.

Подробные примеры каждого оператора см. в разделе Null operators.

Типы значений с поддержкой NULL и ссылочные типы с поддержкой NULL служат различным целям

Типы значений, допускающие значение NULL, и ссылочные типы, допускающие значение NULL, не являются альтернативными. Они решают различные проблемы:

  • Используйте T? для типа значения, который должен представлять "нет значения". Например, используйте int? для необязательного столбца базы данных или для DateTime? события, которое еще не запланировано.
  • Используйте string? и другие аннотации допускающие значение NULL, чтобы показать, что ссылка может быть null, чтобы компилятор мог предупреждать вас до того, как произойдет NullReferenceException во время выполнения.

Вместе эти функции и операторы NULL дают полный набор инструментов для записи кода C#, безопасного со значением NULL.