Ответ 1
Ключ к пониманию, что null!
значит это понимание !
оператор. Возможно, вы использовали его раньше как оператор "не". Но, начиная с С# 8.0, оператор также может использоваться в типе для управления Nullabilty
Что это !
оператор при использовании на тип?
!
Оператор, используемый в типе, называется оператором Null Forgiving [ docs ]. Это было введено в С# 8.0
Техническое объяснение
Типичное использование
Предполагая это определение:
class Person
{
public string? MiddleName;
}
Использование будет:
void LogPerson(Person person)
{
Console.WriteLine(person.MiddleName.Length); // WARNING: may be null
Console.WriteLine(person.MiddleName!.Length); // No warning
}
Этот оператор в основном отключает нулевые проверки компилятором.
Внутренние работы
Использование этого оператора сообщает компилятору, что что-то, что может быть нулевым, безопасно для доступа. Вы выражаете намерение "не заботиться" о нулевой безопасности в этом случае.
Существует 2 состояния, в которых может находиться переменная - когда речь идет о нулевой безопасности.
- Nullable - может быть нулевым
- Не обнуляемый - не может быть нулевым.
Начиная с С# 8.0 все ссылочные типы по умолчанию не обнуляются.
"Обнуляемость" может быть изменена этими 2 новыми операторами типа:
-
!
= ОтNullable
кNon-Nullable
-
?
= ОтNon-Nullable
кNullable
Эти операторы в основном аналогичны друг другу. Компилятор использует информацию, которую вы определили с этими операторами, для обеспечения нулевой безопасности.
?
Использование оператора.
-
Обнуляемая
string? x;
string? x;
-
x
является ссылочным типом - поэтому по умолчанию не обнуляется. - Мы применяем
?
оператор - что делает его обнуляемым. -
x = null
Работает нормально.
-
-
Не обнуляемая
string y;
-
y
является ссылочным типом - поэтому по умолчанию не обнуляется. -
y = null
Генерирует предупреждение, поскольку вы присваиваете нулевое значение чему-то, что не должно быть нулевым.
-
!
Использование оператора.
string x;
string? y = null;
-
x = y
- Нелегальная! -
Warning: "y" may be null
- Левая сторона задания не допускает обнуления, но правая часть обнуляется.
- Нелегальная! -
-
x = y!
- Юридическая!
- Правая и левая сторона задания не могут быть обнулены.
- Работает с тех пор
y!
Применяется!
Операторy
что делает его необнуляемым.
ВНИМАНИЕ
!
Оператор отключает только проверки компилятора на уровне системы типов - во время выполнения значение все еще может быть нулевым.
Это анти-паттерн.
Вы должны стараться избегать использования !
Null-прощающий-оператор.
Существуют допустимые варианты использования (подробно описанные ниже), такие как модульные тесты, в которых этот оператор подходит для использования. Тем не менее, в 99% случаев вам лучше найти альтернативное решение. Пожалуйста, не шлепайте десятки !
в вашем коде, просто чтобы замолчать предупреждения. Подумайте, действительно ли ваше дело оправдывает использование.
Используйте - но с осторожностью. Если нет конкретной цели/варианта использования, предпочитайте не использовать его.
Это сводит на нет эффекты нулевой безопасности, которые вы гарантируете компилятором.
Используя !
Оператор создаст очень трудно найти ошибки. Если у вас есть свойство, которое помечено как необнуляемое, вы будете считать, что можете безопасно его использовать. Но во время выполнения вы неожиданно наталкиваетесь на NullReferenceException
и NullReferenceException
голову. Так как значение фактически стало нулевым после обхода проверок компилятором с помощью !
,
Почему этот оператор существует тогда?
- В некоторых крайних случаях компилятор не может обнаружить, что обнуляемое значение фактически не обнуляемо.
- Более простая миграция кодовой базы.
- В некоторых случаях вам просто все равно, станет ли что-то нулевым.
- При работе с юнит-тестами вы можете захотеть проверить поведение кода при появлении
null
.
Отвечая на ваш вопрос конкретно.
Так что же null!
имею в виду?
Он сообщает компилятору, что null
не является null
значением. Звучит странно, не правда ли?
Это так же, как y!
из примера выше. Это выглядит странно, поскольку вы применяете оператор к null
литералу. Но концепция та же самая.
Разбирая, что происходит.
public string LastName { get; } = null!;
Эта строка определяет необнуляемое свойство класса с именем LastName
типа string
. Так как он не обнуляем, вы технически не можете присвоить ему ноль - очевидно.
Но вы делаете именно это - присвойте null
LastName
- используя !
оператор. Потому что null!
не является нулевым - насколько компилятор обеспокоен нулевой безопасностью.