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

Примечание

В этой статье рассматриваются ссылочные типы, допускающие значение NULL.This article covers nullable reference types. Вы также можете объявить типы значений, допускающие значение NULL.You can also declare nullable value types.

Ссылочные типы, допускающие значение NULL, доступны начиная с C# 8.0, в коде, который дал явное согласие на контекст, поддерживающий значение NULL.Nullable reference types are available beginning with C# 8.0, in code that has opted in to a nullable aware context. Ссылочные типы, допускающие значение NULL, предупреждения о значении NULL при статическом анализе и оператор, опускающий NULL, являются необязательными функциями языка.Nullable reference types, the null static analysis warnings, and the null-forgiving operator are optional language features. По умолчанию все они отключены.All are turned off by default. Контекст, допускающий значение NULL, контролируется на уровне проекта с помощью параметров сборки или в коде с помощью директив pragma.A nullable context is controlled at the project level using build settings, or in code using pragmas.

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

  • переменная ссылочного типа T должна быть инициализирована со значением, отличным от NULL, и ей невозможно присвоить значение, которое может быть равно null;A variable of a reference type T must be initialized with non-null, and may never be assigned a value that may be null.
  • переменная ссылочного типа T? может быть инициализирована с помощью null, или ей может быть присвоено значение null, но она должна быть проверена на null перед разыменованием;A variable of a reference type T? may be initialized with null or assigned null, but is required to be checked against null before de-referencing.
  • переменная m типа T? считается не равной NULL при применении оператора, опускающего NULL, как в m!.A variable m of type T? is considered to be non-null when you apply the null-forgiving operator, as in m!.

Различия между ссылочным типом, не допускающим значение NULL, T и ссылочным типом, допускающим значение NULL, T? проявляются при интерпретации предыдущих правил компилятором.The distinctions between a non-nullable reference type T and a nullable reference type T? are enforced by the compiler's interpretation of the preceding rules. Переменная типа T и переменная типа T? представлены одним и тем же типом .NET.A variable of type T and a variable of type T? are represented by the same .NET type. В следующем примере объявляется строка, не допускающая значение NULL, и строка, допускающая значение NULL, а затем используется оператор, опускающий NULL, для присваивания значения строке, не допускающей значение NULL:The following example declares a non-nullable string and a nullable string, and then uses the null-forgiving operator to assign a value to a non-nullable string:

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

Переменные notNull и nullable представлены типом String.The variables notNull and nullable are both represented by the String type. Так как типы, допускающие и не допускающие значение NULL, хранятся в виде одного типа, существует несколько мест, где использование ссылочного типа, допускающего значение NULL, не допускается.Because the non-nullable and nullable types are both stored as the same type, there are several locations where using a nullable reference type isn't allowed. Как правило, ссылочный тип, допускающий значение NULL, запрещено использовать в качестве базового класса или реализованного интерфейса.In general, a nullable reference type can't be used as a base class or implemented interface. Ссылочный тип, допускающий значение NULL, не может использоваться в выражении проверки типа или создания объекта.A nullable reference type can't be used in any object creation or type testing expression. Ссылочный тип, допускающий значение NULL, не может быть типом выражения доступа к члену.A nullable reference type can't be the type of a member access expression. Эти конструкции показаны в следующих примерах:The following examples show these constructs:

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, и статический анализNullable references and static analysis

Примеры в предыдущем разделе иллюстрируют природу ссылочных типов, допускающих значение NULL.The examples in the previous section illustrate the nature of nullable reference types. Ссылочные типы, допускающие значение NULL, не являются новыми типами классов, а обозначены заметками для существующих ссылочных типов.Nullable reference types aren't new class types, but rather annotations on existing reference types. Компилятор использует эти заметки, чтобы помочь найти потенциальные ошибки для пустых ссылок в коде.The compiler uses those annotations to help you find potential null reference errors in your code. Во время выполнения нет никакой разницы между ссылочным типом, не допускающим значение NULL, и ссылочным типом, допускающим значение NULL.There's no runtime difference between a non-nullable reference type and a nullable reference type. Компилятор не добавляет никакую проверку для ссылочных типов, не допускающих значение NULL, во время выполнения.The compiler doesn't add any runtime checking for non-nullable reference types. Преимущества заключаются в анализе времени компиляции.The benefits are in the compile-time analysis. Компилятор создает предупреждения, помогающие находить и исправлять потенциальные ошибки со значениями NULL в коде.The compiler generates warnings that help you find and fix potential null errors in your code. Вы объявляете свое намерение, и компилятор предупреждает вас, если код нарушает его.You declare your intent, and the compiler warns you when your code violates that intent.

В контексте, допускающем значение NULL, компилятор выполняет статический анализ для переменных любого ссылочного типа, как допускающего, так и не допускающего значение NULL.In a nullable enabled context, the compiler performs static analysis on variables of any reference type, both nullable and non-nullable. Компилятор отслеживает состояние NULL каждой ссылочной переменной в виде не NULL или может быть NULL.The compiler tracks the null state of each reference variable as either not null or maybe null. Состоянием по умолчанию для ссылки, не допускающей значение NULL, является не NULL.The default state of a non-nullable reference is not null. Состоянием по умолчанию для ссылки, допускающей значение NULL, является может быть NULL.The default state of a nullable reference is maybe null.

Ссылочные типы, не допускающие значение NULL, всегда должны быть безопасными для разыменования, так как их состоянием NULL является не NULL.Non-nullable reference types should always be safe to dereference because their null state is not null. Чтобы применить это правило, компилятор выдает предупреждения, если ссылочный тип, не допускающий значение NULL, не инициализируется со значением, отличным от NULL.To enforce that rule, the compiler issues warnings if a non-nullable reference type isn't initialized to a non-null value. Локальные переменные должны присваиваться там же, где они объявляются.Local variables must be assigned where they're declared. Каждый конструктор должен назначить каждое поле либо в своем теле, вызываемом конструкторе, либо с помощью инициализатора поля.Every constructor must assign every field, either in its body, a called constructor, or using a field initializer. Компилятор выдает предупреждения, если ссылка, не допускающая значение NULL, присваивается ссылке с состоянием может быть NULL.The compiler issues warnings if a non-nullable reference is assigned to a reference whose state is maybe null. Однако так как ссылка, не допускающая значение NULL, имеет состояние не NULL, при разыменовании этих переменных предупреждения не выдаются.However, because a non-nullable reference is not null, no warnings are issued when those variables are de-referenced.

Ссылочные типы, допускающие значение NULL, могут быть инициализированы или присвоены null.Nullable reference types may be initialized or assigned to null. Таким образом, статический анализ должен определить, что переменная имеет состояние не NULL, до ее разыменования.Therefore, static analysis must determine that a variable is not null before it's dereferenced. Если ссылка, допускающая значение NULL, определяется как может быть NULL, ее невозможно присвоить ссылочной переменной, не допускающей значение NULL.If a nullable reference is determined to be maybe null, it can't be assigned to a non-nullable reference variable. В следующем классе показаны примеры этих предупреждений:The following class shows examples of these warnings:

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

    public ProductDescription() // Warning! short description 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;
    }
}

В следующем фрагменте кода показано, где компилятор выдает предупреждения при использовании этого класса:The following snippet shows where the compiler emits warnings when using this class:

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 ссылочных переменных.The preceding examples demonstrate the compiler's static analysis to determine the null state of reference variables. Компилятор применяет правила языка для проверок и присваиваний, чтобы получить сведения для анализа.The compiler applies language rules for null checks and assignments to inform its analysis. Компилятор не может делать предположения о семантике методов или свойств.The compiler can't make assumptions about the semantics of methods or properties. При вызове методов, выполняющих проверки значений NULL, компилятор не может понять, что эти методы влияют на состояние NULL переменной.If you call methods that perform null checks, the compiler can't know those methods affect a variable's null state. Существует ряд атрибутов, которые можно добавить в API, чтобы сообщить компилятору о семантике аргументов и возвращаемых значений.There are a number of attributes you can add to your APIs to inform the compiler about the semantics of arguments and return values. Эти атрибуты применялись для многих общих API в библиотеках .NET Core.These attributes have been applied to many common APIs in the .NET Core libraries. Например, IsNullOrEmpty был обновлен, и компилятор правильно интерпретирует этот метод как проверку значения NULL.For example, IsNullOrEmpty has been updated, and the compiler correctly interprets that method as a null check. Дополнительные сведения об атрибутах, применяемых для статического анализа состояния NULL, см. в статье Атрибуты, допускающие значение NULL.For more information about the attributes that apply to null state static analysis, see the article on Nullable attributes.

Задание контекста, допускающего значение NULLSetting the nullable context

Существует два способа управления контекстом, допускающим значение NULL.There are two ways to control the nullable context. На уровне проекта можно добавить параметр <Nullable>enable</Nullable>.At the project level, you can add the <Nullable>enable</Nullable> project setting. В одном файле исходного кода C# можно добавить директиву pragma #nullable enable, чтобы включить контекст, допускающий значение NULL.In a single C# source file, you can add the #nullable enable pragma to enable the nullable context. См. статью о настройке стратегии, допускающей значение NULL.See the article on setting a nullable strategy.

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

Дополнительные сведения см. в следующих предложениях для спецификации языка C#:For more information, see the following proposals for the C# language specification:

См. такжеSee also