Поделиться через


Ссылочные типы, допускающие значение NULL (справочник по C#)

Примечание.

В этой статье рассматриваются ссылочные типы, допускающие значение NULL. Вы также можете объявить типы значений, допускающие значение NULL.

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

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

Документация определяет любую функцию, впервые представленную в последних трех версиях языка или в текущих общедоступных предварительных версиях.

Подсказка

Чтобы узнать, когда функция впервые появилась в C#, ознакомьтесь со статьей по журналу версий языка C#.

Внимание

Все шаблоны проектов включают контекст, допускающий значение NULL, для проекта. Проекты, созданные с помощью предыдущих шаблонов, не включают этот элемент, и эти функции отключены, если вы не включите их в файле проекта или не используете pragmas.

В контексте, поддерживающем значение NULL:

  • Необходимо инициализировать переменную ссылочного типа T с ненулевым значением, и вы никогда не можете назначить значение, которое может быть null.
  • Вы можете инициализировать переменную ссылочного типа T? с null помощью или назначить nullее, но перед отменой ссылок необходимо проверить ее.null
  • При применении оператора null-forgiving к переменной типаT?, как и вm!, переменная m считается ненулевой.

Компилятор применяет различия между типом T ссылок, не допускаемым значением NULL, и типом ссылок T? , допускающего значение NULL, с помощью предыдущих правил. Переменная типа T и переменная типа T? совпадают с типом .NET. В следующем примере объявляется строка, не допускающая значение NULL, и строка, допускающая значение NULL, а затем используется оператор, опускающий NULL, для присваивания значения строке, не допускающей значение NULL:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

Переменные notNull и nullable оба используют String тип. Так как типы, не допускающие значения NULL и допускающие значение NULL, используют один и тот же тип, нельзя использовать ссылочный тип, допускающий значение NULL, в нескольких расположениях. Как правило, нельзя использовать тип ссылок, допускающий значение NULL, как базовый класс или реализованный интерфейс. В выражении создания или тестирования типов объекта нельзя использовать ссылочный тип, допускающий значение NULL. Ссылочный тип, допускающий значение NULL, нельзя использовать в качестве типа выражения доступа к члену. Эти конструкции показаны в следующих примерах:

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

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

Примеры в предыдущем разделе иллюстрируют природу ссылочных типов, допускающих значение NULL. Ссылочные типы, допускающие значение NULL, не являются новыми типами классов, а обозначены заметками для существующих ссылочных типов. Компилятор использует эти заметки, чтобы помочь найти потенциальные ошибки для пустых ссылок в коде. Во время выполнения нет никакой разницы между ссылочным типом, не допускающим значение NULL, и ссылочным типом, допускающим значение NULL. Компилятор не добавляет никакую проверку для ссылочных типов, не допускающих значение NULL, во время выполнения. Преимущества заключаются в анализе времени компиляции. Компилятор создает предупреждения, помогающие находить и исправлять потенциальные ошибки со значениями NULL в коде. Вы объявляете свое намерение, и компилятор предупреждает вас, если код нарушает его.

Внимание

Заметки, допускающие значение NULL, не вводят изменения в поведении, но другие библиотеки могут использовать отражение для создания другого поведения среды выполнения для ссылочных типов, допускающих значение NULL и не допускающих значение NULL. В частности, Entity Framework Core считывает атрибуты, допускающие значение NULL. Он интерпретирует nullable ссылку как необязательное значение, а ненулевую ссылку как обязательное значение.

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

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

Примечание.

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

Можно инициализировать или назначить null ссылочные типы, допускающие значение NULL. Таким образом, статический анализ должен определить, что переменная имеет состояние не равно NULL, до ее разыменования. Если ссылка, допускающая значение NULL, определяется значением NULL, присвоив ему ненулевое значение ссылочной переменной, создается предупреждение компилятора. В следующем классе показаны примеры этих предупреждений:

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

В следующем фрагменте кода показано, где компилятор выдает предупреждения при использовании этого класса:

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

В предыдущих примерах показан статический анализ компилятора для определения состояния NULL ссылочных переменных. Компилятор применяет правила языка для проверок и присваиваний, чтобы получить сведения для анализа. Компилятор не может делать предположения о семантике методов или свойств. При вызове методов, выполняющих проверки значений NULL, компилятор не может понять, что эти методы влияют на состояние NULL переменной. Вы можете добавить атрибуты в API, чтобы сообщить компилятору о семантике аргументов и возвращаемых значений. Многие распространенные API в библиотеках .NET имеют эти атрибуты. Например, компилятор правильно интерпретирует IsNullOrEmpty как проверку null. Дополнительные сведения об атрибутах, применяемых для статического анализа состояния NULL, см. в статье Атрибуты, допускающие значение NULL.

Задание контекста, допускающего значение NULL

Контекст, допускающий значение NULL, можно управлять двумя способами. На уровне проекта добавьте <Nullable>enable</Nullable> параметр проекта. В одном исходном файле C# добавьте #nullable enable pragma, чтобы включить контекст, допускающий значение NULL. См. статью о настройке стратегии, допускающей значение NULL. До .NET 6 новые проекты используют значение по умолчанию, <Nullable>disable</Nullable>. Начиная с .NET 6 все файлы новых проектов содержат элемент <Nullable>enable</Nullable>.

Спецификация языка C#

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

См. также