Обнуляемый null
Рассмотрим следующий код:
public class Person {
public string FirstName { get; }
public string LastName { get; } = null!;
public string? MiddleName {get; } = null;
public Person (string first,
string last, string? middle) {
FirstName = first;
LastName = last;
MiddleName = middle;
}
}
Что значит null! ?Раньше оператор
! означал отрицание в логических выражениях. Но начиная с C#8 в языке допустимо использование обнуляемых ссылочных типов. Поэтому оператор ! приобрёл другое значение и стал называться null-снисходительным (null-forgiving). Свойство
MiddleName обнуляемое (string?), т.к. у человека может не быть отчества. Тогда:Console.WriteLine(person.MiddleName.Length);выдаст предупреждение, что
MiddleName может быть null, аConsole.WriteLine(person.MiddleName!.Length);не выдаст предупреждения из-за null-снисходительного оператора.
Таким образом программист сообщает компилятору, что он уверен, что здесь значение переменной не будет
null.Можно рассматривать операторы
? и ! как приведение типов? = из необнуляемого в обнуляемый! = из обнуляемого в необнуляемыйНапример:
string? x;
? делает из необнуляемого string обнуляемый тип, поэтому можно использовать x = null.string y;Здесь
y = null приводит к предупреждению компилятора, из-за присваивания null необнуляемому типу. Аналогично y = x приведёт к предупреждению, т.к. x может быть null. Однако y = x! не вызовет предупреждения, т.к. мы «гарантируем», что x не null.ВНИМАНИЕ: оператор
! отключает только проверку компилятора на уровне системы типов. Во время выполнения значение может быть null. Поэтому использование null-снисходительного оператора может быть признаком плохого дизайна системы, поскольку он сводит на нет все усилия проверки на null-безопасность, которую выполняет компилятор.Использование оператора
! может создать очень трудные для поиска ошибки. Если у вас есть свойство, помеченное как необнуляемое, предполагается, что его можно использовать безопасно. Но во время выполнения вы можете неожиданно столкнуться с исключением NullReferenceException, поскольку значение фактически стало null из-за обхода проверок компилятора с помощью оператора !.Зачем тогда он нужен?
Существуют допустимые варианты использования, в которых использование является целесообразным:
- В некоторых (редких) случаях компилятор не может определить, что обнуляемое значение на самом деле не может быть
null.- Для упрощения миграции старого кода, не использовавшего обнуляемые ссылочные типы.
- В некоторых случаях вас просто не волнует, что что-то будет
null.- В тестах для проверки поведения кода при получении
null.Так что же такое
null! ?null! сообщает компилятору, что null не допускает значения null. Странно, правда? Но это то же самое, что и x! из примера выше. Литерал null допускает значение null по определению. Но, как мы узнали, обнуляемость любого типа можно переопределить с помощью оператора !.Систему типов не волнует фактическое значении переменной во время выполнения. Её заботит только тип переменной времени компиляции. И в исходном примере свойство
LastName (null!) не допускает значения null, что допустимо для системы типов.Источник: https://stackoverflow.com/questions/54724304/what-does-null-statement-mean