Полное руководство по языку программирования С# 14 и платформе .NET 10 / Классы, структуры и пространства имен

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

Начальный ⏱ 5 мин чтения 👁 2 🕓 обновлено 11.08.2026

Кроме стандартных значений типа чисел, строк, язык C# имеет специальное значение - null, которое фактически указывает на отсутствие значения как такового, отсутствие данных. До сих пор значение null выступает как значение по умолчанию для ссылочных типов.

До версии C# 8.0 всем ссылочным типам спокойно можно было присваивать значение null:


string name = null;
Console.WriteLine(name); 

Но начиная с версии C# 8.0 в язык была введена концепция ссылочных nullable-типов (nullable reference types) и nullable aware context - nullable-контекст, в котором можно использовать ссылочные nullable-типы.

Чтобы определить переменную/параметр ссылочного типа, как переменную/параметр, которым можно присваивать значение null, после названия типа указывается знак вопроса ?


string? name = null;
Console.WriteLine(name);	// ничего не выведет

К примеру встроенный метод Console.ReadLine(). который считывает с консоли строку, возвращает именно значение string?, а не просто string:

string? name = Console.ReadLine();

Зачем нужно это значение null? В различных ситуациях бывает удобно, чтобы объекты могли принимать значение null, то есть были бы не определены. Стандартный пример - работа с базой данных, которая может содержать значения null. И мы можем заранее не знать, что мы получим из базы данных - какое-то определенное значение или же null.

При этом подобные ссылочные типы, которые допускают присвоение значения null, доступно только в nullable-контексте. Для nullable-контекста характерны следующие особенности:

  • Переменную ссылочного типа следует инициализировать конкретным значением, ей не следует присваивать значение null

  • Переменной ссылочного nullable-типа можно присвоить значение null, но перед использование необходимо проверять ее на значение null.

Начиная с .NET 6 и C# 10 nullable-контекст по умолчанию распространяется на все файлы кода в проекта. Например, если мы наберем в Visual Studio 2022 для проекта .NET 6 предыдущий пример, то мы столкнемся с предупреждением:

Хотя nullable-контекст - это опция, которой мы можем управлять. Так, откроем файл проекта. Для этого либо двойным кликом левой кнопкой мыши нажмем на проект, либо нажмем на проект правой кнопкой мыши и в появившемся меню выберем пункт Edit Project File

После этого Visual Studio откроет нам файл проекта, который будет выглядеть примерно следующим образом:


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

Здесь строка

<Nullable>enable</Nullable>

точнее элемент <Nullable> со значением enable указывает, что эта nullable-контекст будет распространяться на весь проект.

Чем так плох null? Дело в том, что это значение означает, отсутствие данных. Но, допустим, у нас есть ситуация, когда мы получаем извне некоторую строку и пытаемся обратиться к ее функциональности. Например, в примере ниже у строки вызывается метод ToUpper(), который переводит все символы строки в верхний регистр:


string name = null;
PrintUpper(name);  // ! NullReferenceException

void PrintUpper(string text)
{
    Console.WriteLine(text.ToUpper());
}

Здесь при выполнении вызова PrintUpper(name) мы столкнемся с исключением NullReferenceException, и программа аварийно завершит свою работу. Кто-то может сказать, что ситуация искуственная - мы же явно знаем, что в функцию передается null. Однако в реальности данные могут приходить извне, например, из базы данных, откуда-то из сети и т.д. И мы можем явно не знать, есть ли в реальности данные или нет. И использование ссылочных nullable-типов позволяет частично решить эту ситуацию. Частично - поскольку предупреждения все равно не мешают нам скомпилировать и запустить программу выше. Однако nullable-контекст позволяет воспользоваться возможностями статического анализа, благодаря которому можно увидеть потенциально опасные куски кода, где мы можем столкнуться с NullReferenceException.

Кроме того, есть вероятность, что Microsoft изменит отношение в отношении null и NullReferenceException, и подобные предупреждения превратятся в будущих версиях в ошибки, поэтому лучше уже сейчас быть к этому готовым

Например, изменим предыдущий пример следующим образом:


string? name = null;
PrintUpper(name);  // 

void PrintUpper(string? text)
{
    Console.WriteLine(text.ToUpper());
}

Здесь статический анализ подскажет, что в методе PrintUpper потенциально опасная ситуация, поскольку параметр text может быть равен null.

Отключение nullable-контекста

Для отключения nullable-контекста в файле конфигурации проекта достаточно изменить значение опции Nullable, например, на "disable":


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>disable</Nullable>
  </PropertyGroup>

</Project>

Отключив nullable-контекст, мы больше не сможем использовать в файлах кода в проекте ссылочные nullable-типы и соответственно воспользоваться встроенным статическим анализом потенциально опасных ситуаций, где можно столкнуться с NullReferenceException.

nullable-контекст на уровне участка кода

Мы также можем включить nullable-контекст на урове отдельных участков кода с помощью директивы #nullable enable. Допустим, глобально у нас отключен nullable-контекст:

<Nullable>disable</Nullable>

Определим в файле Program.cs следующий код:


#nullable enable // включаем nullable-контекст на уровне файла

string? name = null;

PrintUpper(name);

void PrintUpper(string? text)
{
    Console.WriteLine(text.ToUpper());
}

Первая строка позволяет включить на уровне всего файла nullable-контекст.

Оператор ! (null-forgiving operator)

Оператор ! (null-forgiving operator) позволяет указать, что переменная ссылочного типа не равна null:


string? name = null;

PrintUpper(name!);

void PrintUpper(string text)
{
    if(text == null) Console.WriteLine("null");
    else Console.WriteLine(text.ToUpper());
}

Здесь если бы мы не использовали оператор !, а написали бы PrintUpper(name), то компилятор высветил бы нам предупреждение. Но в самом методе мы итак проверяем на null, поэтому даже если в метод передается null, то мы не столкнемся ни с какими проблемами. И чтобы убрать ненужное предупреждение, применяется данный оператор. То есть данный оператор не оказывает никакого влияния во время выполнения кода и предназначен только для статического анализа компилятора. Во время выполнения выражение name! будет аналогично значению name

Исключение кода из nullable-контекста

С помощью специальной директивы #nullable disable можно исключить какой-то определенный кусок кода из nullable-контекста. Например:


#nullable disable
	string text = null;	// здесь nullable-контекст не действует
#nullable restore

string? name = null;   // здесь nullable-контекст снова действует

Любой код между директивами #nullable disable и #nullable restore будет исключен из nullable-контекста и тем самым не будет подлежать статическому анализу.

Проверь себя

📋 Тест по теме «Null и ссылочные типы» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

string _____ = null;
Console.WriteLine(name);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

string? name = null;
_____.WriteLine(name);	// ничего не выведет
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

string? name = Console._____();
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

<Project Sdk="Microsoft._____.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

<Nullable>_____</Nullable>
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

string name = null;
_____._____(name);
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

string? _____ = null;
Console._____(name);	// ничего не выведет
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

<Project Sdk="Microsoft.NET.Sdk">
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
</Project>
    <OutputType>Exe</OutputType>
  <PropertyGroup>
  </PropertyGroup>
    <Nullable>enable</Nullable>
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Null и ссылочные типы»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Null и ссылочные типы», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

string name = null;
Console.WriteLine(name);
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Null и ссылочные типы»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

string? name = null;
Console.WriteLine(name);	// ничего не выведет
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Null и ссылочные типы»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Null и ссылочные типы»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Null и ссылочные типы». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.