Ссылочные типы, допускающие значение null
В контексте, допускающем значение NULL, все ссылочные типы были пустыми. Ссылочные типы , допускающие значение NULL, относятся к группе функций, включенных в контексте с поддержкой NULL, что позволяет свести к минимуму вероятность возникновения System.NullReferenceExceptionв коде среды выполнения. Ссылочные типы, допускающие значения NULL, содержат три функции, которые помогают избежать возникновения этих исключений, а также возможность явно помечать ссылочный тип как допускающий значение NULL.
- Улучшен анализ статического потока, определяющий, может ли переменная быть
null
перед отменой ссылки. - Атрибуты, аннотирующие API, чтобы анализ потока определял состояние NULL.
- Аннотации переменных, которые разработчики используют для явного объявления предполагаемого состояния NULL для переменной.
Компилятор отслеживает состояние NULL каждого выражения в коде во время компиляции. Состояние NULL имеет одно из трех значений:
- not-null: выражение, как известно, не является-
null
. - может иметь значение NULL: выражение может быть
null
. - забвение: компилятор не может определить состояние NULL выражения.
Примечания переменных определяют допустимость значений NULL переменной ссылочного типа:
- непустимый: если вы назначаете
null
значение или выражение, возможно, null переменной, компилятор выдает предупреждение. Переменные, не допускающие значение NULL, имеют значение null-state of not-NULL. - Значение NULL: можно назначить
null
значение или выражение, возможно, null переменной. Если значение null-состояния переменной может иметь значение NULL, компилятор выдает предупреждение, если вы разоменовываете переменную. Состояние NULL по умолчанию для переменной может иметь значение NULL. - забвение: можно назначить
null
значение или выражение, возможно, null переменной. Компилятор не выдает предупреждений при расшифровки переменной или при назначении выражения с значением NULL переменной.
Забвение состояния NULL и забвение значения NULL соответствует поведению до появления ссылочных типов, допускающих значение NULL. Эти значения полезны во время миграции или когда приложение использует библиотеку, которая не включила ссылочные типы, допускающие значение NULL.
Анализ состояния NULL и заметки переменных отключены по умолчанию для существующих проектов, что означает, что все ссылочные типы продолжают иметь значение NULL. Начиная с .NET 6 они включены по умолчанию для новых проектов. Сведения о включении этих функций путем объявления контекста аннотации, допускающего значение NULL, см. в статье Контексты, допускающие значение NULL.
В остальной части этой статьи описывается, как эти три области функций работают для создания предупреждений, когда код может быть разоменовывая null
значение. Разыменование переменной означает доступ к одному из ее членов с помощью оператора .
(точка), как показано в следующем примере:
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"
При разыменовании переменной, значение которой равно null
, среда выполнения создает исключение System.NullReferenceException.
Аналогичным образом можно создавать предупреждения, если []
нотация используется для доступа к члену объекта, если объект имеет null
следующий тип:
using System;
public class Collection<T>
{
private T[] array = new T[100];
public T this[int index]
{
get => array[index];
set => array[index] = value;
}
}
public static void Main()
{
Collection<int> c = default;
c[10] = 1; // CS8602: Possible derefence of null
}
Вы узнаете:
- Анализ состояния null компилятора: как компилятор определяет, является ли выражение не null или, возможно, null.
- Атрибуты , применяемые к API, которые предоставляют больше контекста для анализа состояния null компилятора.
- Заметки переменных, допускающие значение NULL, которые предоставляют сведения о намерении переменных. Заметки полезны для полей, чтобы задать состояние NULL по умолчанию в начале методов члена.
- Правила, управляющие аргументами универсального типа. Добавлены новые ограничения, так как параметры типа могут быть ссылочными типами или типами значений. Суффикс
?
реализуется по-разному для типов значений, допускающих значение NULL, и ссылочных типов, допускающих значение NULL. - Контексты , допускающие значение NULL, помогают перенести большие проекты. Вы можете включить контексты или предупреждения, допускающие значение NULL, в частях приложения при миграции. После устранения дополнительных предупреждений можно включить ссылочные типы, допускающие значение NULL для всего проекта.
Наконец, вы узнаете известные ловушки для анализа состояния NULL в struct
типах и массивах.
Эти понятия также можно изучить в модуле Learn по безопасности, допускаемой null, в C#.
Анализ состояния NULL
Если включены ссылочные типы, допускающие значение NULL, анализ состояния NULL отслеживает состояние ссылок. Выражение имеет значение not-null или , возможно, null. Компилятор определяет, что переменная not-null, двумя способами:
- Переменная была назначена значением, которое, как известно, не имеет значения NULL.
- Переменная была проверена на предмет
null
и не изменялась с момента проверки.
Если ссылочные типы, допускающие значение NULL, не включены, все выражения имеют состояние NULL для забвения. В остальной части раздела описывается поведение при включении ссылочных типов, допускающих значение NULL.
Любая переменная, которую компилятор не определил как not-null, считается maybe-null. Анализ предоставляет предупреждения в ситуациях, когда может случайно разыменовыть null
значение. Компилятор создает предупреждения на основе состояния NULL.
- Если переменная не имеет значения NULL, эта переменная может быть удалена безопасно.
- Если это переменная maybe-null, ее необходимо проверить, чтобы убедиться, что она не равна
null
, перед разыменованием.
Рассмотрим следующий пример:
string message = null;
// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");
var originalMessage = message;
message = "Hello, World!";
// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");
// warning!
Console.WriteLine(originalMessage.Length);
В предыдущем примере компилятор определяет, что переменная message
maybe-null при выводе первого сообщения. Для второго сообщения предупреждение не выводится. Последняя строка кода выдает предупреждение, поскольку originalMessage
может иметь значение NULL. В следующем примере показано более практичное использование для обхода дерева узлов до корня с обработкой каждого узла во время обхода:
void FindRoot(Node node, Action<Node> processNode)
{
for (var current = node; current != null; current = current.Parent)
{
processNode(current);
}
}
Предыдущий код не создает предупреждений для разыменования переменной current
. Статический анализ определяет, что переменная current
никогда не будет разыменована, если она maybe-null. Переменная current
проверяется на null
до доступа к current.Parent
и перед передачей current
в действие ProcessNode
. В предыдущих примерах показано, как компилятор определяет состояние NULL для локальных переменных при инициализации, назначении или сравнении с null
.
Анализ состояния NULL не отслеживается в вызываемые методы. В результате поля, инициализированные в общем вспомогательном методе, вызываемом всеми конструкторами, создают предупреждение со следующим шаблоном:
Ненулевое свойство name должно содержать ненулевое значение при выходе конструктора.
Эти предупреждения можно устранить одним из двух способов: цепочка конструкторов или атрибуты , допускающие значение NULL, в вспомогательном методе. В приведенном ниже коде показан пример каждого метода. Класс Person
использует общий конструктор, вызываемый всеми другими конструкторами. Класс Student
содержит вспомогательный метод, аннотированный атрибутом System.Diagnostics.CodeAnalysis.MemberNotNullAttribute :
using System.Diagnostics.CodeAnalysis;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public Person() : this("John", "Doe") { }
}
public class Student : Person
{
public string Major { get; set; }
public Student(string firstName, string lastName, string major)
: base(firstName, lastName)
{
SetMajor(major);
}
public Student(string firstName, string lastName) :
base(firstName, lastName)
{
SetMajor();
}
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
Примечание.
В C# 10 были добавлены ряд улучшений для определенного назначения и анализа состояния NULL. При обновлении до C# 10 сократится число ложноположительных результатов с предупреждением о NULL. Дополнительные сведения об улучшениях см. в статье о спецификации функций для определенного присваивания.
Анализ состояния, допускающий значение NULL, и предупреждения компилятора помогают избежать ошибок программы путем расшифровки null
. В статье по разрешению предупреждений, допускающих значение NULL, приведены методы исправления предупреждений, скорее всего, которые были замечены в коде.
Атрибуты в сигнатурах API
Анализ состояния NULL требует указания от разработчиков, чтобы понять семантику API. Некоторые API обеспечивают проверку значений NULL и должны изменять состояние NULL переменной с maybe-null на not-null. Другие API возвращают выражения, которые являются not-null или maybe-null в зависимости от состояния NULL входных аргументов. Например, рассмотрим следующий код, отображающий сообщение в верхнем регистре:
void PrintMessageUpper(string? message)
{
if (!IsNull(message))
{
Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
}
}
bool IsNull(string? s) => s == null;
Основываясь на проверке, любой разработчик будет считать этот код безопасным и не должен создавать предупреждения. Однако компилятор не знает, что IsNull
предоставляет проверку null и выдает предупреждение для message.ToUpper()
инструкции, учитывая message
, что это может быть переменная null . NotNullWhen
Используйте атрибут для исправления этого предупреждения:
bool IsNull([NotNullWhen(false)] string? s) => s == null;
Этот атрибут сообщает компилятору, что, если IsNull
возвращается false
, параметр s
не имеет значения NULL. Компилятор изменяет состояние message
NULL на непустую if (!IsNull(message)) {...}
внутри блока. Предупреждения не выдаются.
Атрибуты предоставляют подробные сведения о состоянии null аргументов, возвращаемых значений и членов экземпляра объекта, используемого для вызова элемента. Подробные сведения о каждом атрибуте можно найти в справочнике по языку об атрибутах ссылки, допускающих значения NULL. По состоянию на .NET 5 все API среды выполнения .NET аннотируются. Статический анализ можно улучшить, аннотировав API, чтобы предоставить семантическую информацию о состоянии NULL аргументов и возвращаемых значений.
Аннотации для переменных, допускающих значения NULL
Анализ состояния NULL обеспечивает надежный анализ локальных переменных. Компилятору требуются дополнительные сведения о переменных элементов. Компилятору требуется дополнительная информация, чтобы задать состояние NULL всех полей в открывающей скобке элемента. Любой из доступных конструкторов можно использовать для инициализации объекта. Если для поля элемента можно задать значение null
, в начале каждого метода компилятор должен предположить, что его состояние NULL — maybe-null.
Используйте аннотации, которые могут объявлять, является ли переменная ссылочным типом, допускающим значение NULL, или ссылочным типом, не допускающим значения NULL. Эти аннотации указывают на состояние NULL для переменных:
- Ссылка не должна иметь значение NULL. Состояние ссылочной переменной, не допускающей значения NULL, по умолчанию не равно NULL. Компилятор применяет правила, обеспечивающие безопасность разыменовки этих переменных, не проверяя, не имеет ли значение NULL:
- Переменную необходимо инициализировать со значением, отличным от NULL.
- Переменной не может быть присвоено значение
null
. Компилятор выдает предупреждение, когда код присваивает выражение maybe-null переменной, которая не должна иметь значение NULL.
- Ссылка может иметь значение NULL. Состоянием по умолчанию для ссылочной переменной, допускающей значение NULL, является может быть NULL. Компилятор применяет правила, чтобы убедиться, что вы правильно проверяете ссылку
null
:- Переменная может быть разоменовано только в том случае, если компилятор может гарантировать, что значение не
null
является. - Эти переменные могут быть инициализированы со значением по умолчанию
null
и могут быть назначены вnull
другом коде. - Компилятор не выдает предупреждения при назначении кода может быть null-выражение переменной, которая может иметь значение NULL .
- Переменная может быть разоменовано только в том случае, если компилятор может гарантировать, что значение не
Любая непустая ссылочная переменная имеет состояние NULL по умолчанию не null. Любая переменная, допускаемая значение NULL, имеет начальное состояние NULL, возможно , null.
Ссылочный тип, допускающий значение NULL использует тот же синтаксис, что и тип значения, допускающего значение NULL: к типу переменной добавляется ?
. Например, следующее объявление переменной представляет строковую переменную, допускающую значение NULL, name
:
string? name;
Если включены ссылочные типы, допускающие значение NULL, любая переменная, в которой ?
не добавляется имя типа, является ненулевой ссылочным типом. Это включает все переменные ссылочного типа в существующем коде после включения этой функции. Однако любые неявные типизированные локальные переменные (объявленные с помощью var
) являются ссылочными типами, допускающими значение NULL. Как показано в предыдущих разделах, статический анализ определяет состояние NULL локальных переменных, чтобы определить, может ли они иметь значение NULL перед разыменовыванием.
Иногда необходимо переопределить предупреждение, если известно, что переменная не имеет значение NULL, но компилятор определяет ее состояние NULL как maybe-null. Используйте оператор, допускающий значение NULL !
перед именем переменной, чтобы принудительно задать для состояния NULL not-null. Например, если вы знаете, что переменная name
не имеет значение null
, а компилятор выдает предупреждение, напишите следующий код, чтобы переопределить анализ компилятора:
name!.Length;
Ссылочные типы, допускающие значение NULL, и типы значений, допускающие значение NULL, предоставляют аналогичную семантическую концепцию: переменная может представлять собой значение или объект или может быть null
. Однако ссылочные типы, допускающие значение NULL, и типы значений, допускающие значение NULL, реализуются по-разному: типы значений, допускающие значение NULL, реализуются с помощью System.Nullable<T>, а ссылочные типы, допускающие значение NULL, реализуются атрибутами, которые считывает компилятор. Например, string?
и string
представлены одним и тем же типом: System.String. Однако int?
и int
представлены System.Nullable<System.Int32>
и System.Int32 соответственно.
Ссылочные типы, допускающие значение NULL, являются функцией времени компиляции. Это означает, что вызывающие пользователи могут игнорировать предупреждения, намеренно использовать null
в качестве аргумента для метода, ожидающего ненулевой ссылки. Авторы библиотеки должны включать проверки во время выполнения в отношении значений аргументов NULL. Это ArgumentNullException.ThrowIfNull предпочтительный вариант проверки параметра на значение NULL во время выполнения.
Внимание
Включение заметок, допускающих значение NULL, может изменить способ определения необходимости элемента данных Entity Framework Core. Дополнительные сведения см. в статье об основах Entity Framework Core: работа с типами ссылок, допускающих значение NULL.
Универсальные шаблоны
Для универсальных шаблонов требуются подробные правила для обработки T?
для любых типов параметров T
. Правила должны быть подробными, учитывая журнал и другую реализацию типа значения, допускающего значение NULL, и ссылочного типа, допускающего значение NULL. Типы значений, допускающие значение NULL, реализуются с помощью структуры System.Nullable<T>. Ссылочные типы, допускающие значения NULL, реализуются как аннотации типа, предоставляющие компилятору семантические правила.
- Если аргумент типа для
T
является ссылочным типом,T?
ссылается на соответствующий тип ссылки, допускающий значение NULL. Например, еслиT
равенstring
, тоT?
—string?
. - Если аргумент типа для
T
является типом значения,T?
ссылается на тот же тип значения,T
. Например, еслиT
имеет значениеint
, тоT?
также являетсяint
. - Если аргумент типа для
T
является ссылочным типом, допускающим значение NULL,T?
ссылается на тот же ссылочный тип, допускающий значение NULL. Например, еслиT
равенstring?
, тоT?
—string?
. - Если аргумент типа для
T
является типом значения, допускающим значение NULL,T?
ссылается на тот же тип значения, допускающий значение NULL. Например, еслиT
равенint?
, тоT?
—int?
.
Для возвращаемых значений T?
эквивалентно [MaybeNull]T
; для значений аргументов аргумент T?
эквивалентен [AllowNull]T
. Дополнительные сведения см. в статье об атрибутах для анализа состояния NULL в справочнике по языку.
Можно указать другое поведение с помощью ограничений.
- Ограничение
class
означает, чтоT
должен быть ссылочным типом, не допускающим значения NULL (например,string
). Компилятор выдает предупреждение при использовании ссылочного типа, допускающего значение NULL, напримерstring?
дляT
. - Это ограничение
class?
означает, чтоT
должен быть ссылочным типом, не допускающим значение NULL (string
), или ссылочным типом, допускающим значение NULL (например,string?
). Если параметр типа является ссылочным типом, допускающим значение NULL, напримерstring?
, выражениеT?
ссылается на тот же ссылочный тип, допускающий значение NULL, напримерstring?
. - Ограничение
notnull
означает, чтоT
должен быть ссылочным типом, не допускающим значения NULL, или типом значения, не допускающим значения NULL. Если для параметра типа используется ссылочный тип, допускающий значение NULL, или тип значения, допускающий значение NULL, компилятор выдает предупреждение. Кроме того, еслиT
является типом значения, возвращаемое значение относится к этому типу значения, а не к соответствующему типу значения, допускающему значение NULL.
Эти ограничения помогают предоставить дополнительные сведения компилятору о том, как T
используется. Это помогает, когда разработчики выбирают тип для T
и обеспечивают лучший анализ состояния NULL при использовании экземпляра универсального типа.
Контексты допустимости значения NULL
Для небольших проектов можно включить ссылочные типы, исправить предупреждения и продолжить. Однако для более крупных проектов и решений с несколькими проектами, которые могут создавать большое количество предупреждений. Вы можете использовать pragmas для включения ссылочных типов, допускающих значение NULL, по мере начала использования ссылочных типов, допускающих значение NULL. При включении в существующей базе кода новые функции, которые защищают от исключения System.NullReferenceException, могут нарушать работу:
- Все явно типизированные ссылочные переменные обрабатываются как ссылочные типы, не допускающие значения NULL.
- Значение ограничения
class
в универсальных шаблонах изменилось на ссылочный тип, не допускающий значения NULL. - В связи с новыми правилами создаются новые предупреждения.
Контекст аннотаций, допускающий значение NULL, определяет поведение компилятора. Существует четыре значения для контекста аннотаций, допускающего значение NULL:
- disable: код является пустым и неумышленным. Отключите поведение до включения ссылочных типов, допускающих значение NULL, за исключением того, что новый синтаксис выдает предупреждения вместо ошибок.
- Предупреждения о значении NULL отключены.
- Все переменные ссылочного типа являются ссылочными типами, допускающими значение NULL.
- При использовании суффикса
?
для объявления ссылочного типа, допускающего значение NULL, выдается предупреждение. - Можно использовать оператор, допускающий NULL,
!
, но это ни на что не повлияет.
- enable: компилятор включает весь анализ ссылок null и все языковые функции.
- Включены все новые предупреждения о значениях NULL.
- Для объявления ссылочного типа, допускающего значение NULL, можно использовать суффикс
?
. - Переменные ссылочного типа без
?
суффикса являются ненулевыми ссылочными типами. - Оператор прощения null подавляет предупреждения о возможном разыменовании
null
.
- warnings: компилятор выполняет полный анализ значений NULL и выдает предупреждения, если код может разыменовать
null
.- Включены все новые предупреждения о значениях NULL.
- При использовании суффикса
?
для объявления ссылочного типа, допускающего значение NULL, выдается предупреждение. - Все переменные ссылочного типа могут иметь значение NULL. Однако элементы имеют состояние NULL not-null в открывающей фигурной скобке всех методов, если только они не объявлены с суффиксом
?
. - Может использовать оператор, допускающий NULL,
!
.
- заметки. Компилятор не выдает предупреждения, когда код может разыменовывать
null
или при назначении выражения с значением NULL переменной, не допускающей значения NULL.- Все новые предупреждения о значении NULL отключены.
- Для объявления ссылочного типа, допускающего значение NULL, можно использовать суффикс
?
. - Переменные ссылочного типа без
?
суффикса являются ненулевыми ссылочными типами. - Можно использовать оператор, допускающий NULL,
!
, но это ни на что не повлияет.
Контекст аннотаций о допустимости значения NULL и контекст с предупреждениями о допустимости значения NULL можно задать для проекта с помощью элемента <Nullable>
в файле CSPROJ. Этот элемент настраивает, как компилятор интерпретирует допустимость значений NULL для типов и какие предупреждения выдаются. В следующей таблице показаны допустимые значения и приводится сводка по задаваемым контекстам.
Контекст | Предупреждения об разыменовании | Предупреждения о назначении | Типы ссылок | Суффикс ? |
Оператор ! |
---|---|---|---|---|---|
disable |
Выключено | Выключено | Все допускают значение NULL | Выдает предупреждение | Не оказывает влияния |
enable |
Включен | Включен | Не допускают значение NULL, если не объявлены с помощью ? |
Объявляет тип, допускающий значение NULL | Подавляет предупреждения о возможном назначении null |
warnings |
Включен | Нет данных | Все имеют значение NULL, но члены считаются не null при открытии фигурной скобки методов | Выдает предупреждение | Подавляет предупреждения о возможном назначении null |
annotations |
Выключено | Выключено | Не допускают значение NULL, если не объявлены с помощью ? |
Объявляет тип, допускающий значение NULL | Не оказывает влияния |
Переменные ссылочного типа в коде, скомпилированном в отключенном контексте, являются пустыми. Вы можете назначить null
литерал или переменную с значением NULL переменной, которая является ненуловимой. Однако по умолчанию для переменной со свойством nullable oblivious установлено состояние не равно NULL.
Вы можете выбрать оптимальный параметр для своего проекта:
- Выберите отключение для устаревших проектов, которые не нужно обновлять на основе диагностика или новых функций.
- Выберите предупреждения , чтобы определить, где может вызываться System.NullReferenceExceptionкод. Вы можете устранить эти предупреждения перед изменением кода, чтобы включить ссылочные типы, не допускающие значения NULL.
- Выберите annotations, чтобы выразить свое намерение, прежде чем включать предупреждения.
- Выберите для новых проектов и активных проектов, где требуется защитить от исключений ссылок null.
Пример:
<Nullable>enable</Nullable>
Также можно использовать директивы для задания этих контекстов в любом месте в исходном коде. Эти директивы наиболее полезны при переносе большой базы кода.
#nullable enable
: задает контекст заметки, допускающий значение NULL, и контекст предупреждения, допускающий значение NULL, для включения.#nullable disable
: задает контекст заметки, допускающий значение NULL, и контекст предупреждения, допускающий значение NULL, для отключения.#nullable restore
: восстанавливает контекст заметки, допускающий значение NULL, и контекст предупреждения, допускающий значение NULL, в параметры проекта.#nullable disable warnings
: задайте для отключения контекста предупреждения, допускающего значение NULL.#nullable enable warnings
: задайте контекст предупреждения, допускающего значение NULL, для включения.#nullable restore warnings
: восстанавливает контекст предупреждения, допускающего значение NULL, в параметры проекта.#nullable disable annotations
: задайте для отключения контекста заметки, допускающий значение NULL.#nullable enable annotations
: задайте для включения контекста заметки, допускающий значение NULL.#nullable restore annotations
: восстанавливает контекст заметки, допускающий значение NULL, в параметры проекта.
Для любой строки кода можно задать любое из следующих сочетаний:
Контекст предупреждения | Контекст аннотаций | Использование |
---|---|---|
проект по умолчанию | проект по умолчанию | По умолчанию. |
включить | disable | Исправление предупреждения при анализе |
включить | проект по умолчанию | Исправление предупреждения при анализе |
проект по умолчанию | включить | Добавление аннотации для типа |
включить | включить | Код уже перенесен |
disable | включить | Добавление аннотации к коду перед исправлением предупреждений |
disable | disable | Добавление устаревшего кода в перенесенный проект |
проект по умолчанию | disable | Редко |
disable | проект по умолчанию | Редко |
Эти девять сочетаний позволяют точно контролировать диагностику, выдаваемую компилятором для кода. Вы можете включить дополнительные функции в любой области, которую вы обновляете, без просмотра дополнительных предупреждений, которые вы еще не готовы к устранению.
Внимание
Глобальный контекст, допускающий значения NULL, не применяется для созданных файлов кода. В любом случае контекст, допускающий значение NULL, отключен для любого исходного файла, помеченного как созданный. Это означает, что все интерфейсы API в создаваемых файлах не заносятся в заметки. Существует четыре способа пометки файла как созданного:
- В файле. editorconfig укажите
generated_code = true
в разделе, который применяется к этому файлу. - Вставьте
<auto-generated>
или<auto-generated/>
в комментарий в верхней части файла. Он может находиться в любой строке комментария, однако блок комментариев должен быть первым элементом в файле. - Имя файла следует начинать с TemporaryGeneratedFile_
- В конце имени файла следует указать .designer.cs, .generated.cs, .g.cs или .g.i.cs.
Генераторы могут явно использовать директиву препроцессора #nullable
.
По умолчанию контексты с заметками и предупреждениями о допустимости значения NULL отключены. Это означает, что существующий код компилируется без изменений и без создания новых предупреждений. Начиная с .NET 6 во все шаблоны новых проектов входит элемент <Nullable>enable</Nullable>
.
Эти параметры предоставляют две отдельные стратегии для обновления существующей базы кода так, чтобы она могла использовать ссылочные типы, допускающие значение NULL.
Известные ошибки
Массивы и структуры, содержащие ссылочные типы, являются известными проблемами в ссылках, допускающих значения NULL, и статическом анализе, который определяет безопасность значения NULL. В обоих ситуациях ссылка, не допускающая значение NULL, может быть инициализирована без null
создания предупреждений.
Структуры
Структуре, которая содержит ссылочные типы, не допускающие значения NULL, может быть присвоено значение default
без предупреждения. Рассмотрим следующий пример:
using System;
#nullable enable
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default);
}
В предыдущем примере в PrintStudent(default)
не возвращается предупреждение, хотя ссылочные типы FirstName
и LastName
, не допускающие значения NULL, имеют значение NULL.
Еще один более распространенный случай связан с работой с универсальными структурами. Рассмотрим следующий пример:
#nullable enable
public struct S<T>
{
public T Prop { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(S<string>).Prop;
}
}
В предыдущем примере свойство Prop
находится null
во время выполнения. Она назначается строке без каких-либо предупреждений, не допускающих значение NULL.
Массивы
При использовании ссылочных типов, допускающих значения NULL, также могут возникать известные ошибки, связанные с массивами. Рассмотрим следующий пример, в котором не выдаются предупреждения:
using System;
#nullable enable
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
В предыдущем примере объявление массива показывает, что он содержит строки, не допускающие значения NULL, а все элементы инициализируются с использованием значения null
. После этого переменной s
присваивается значение null
(первый элемент массива). Наконец, переменная s
разыменовывается, в результате чего во время выполнения возникает исключение.