Прочитав этот вопрос на HNQ, я продолжил читать о Nullable Reference Types в C # 8 и провел несколько экспериментов.
Я очень хорошо знаю, что 9 раз из 10, или даже чаще, когда кто-то говорит: «Я нашел ошибку компилятора!» это на самом деле дизайн, и их собственное недоразумение. И так как я начал изучать эту функцию только сегодня, очевидно, я не очень хорошо понимаю ее. Теперь, давайте посмотрим на этот код:
#nullable enable
class Program
{
static void Main()
{
var s = "";
var b = s == null; // If you comment this line out, the warning on the line below disappears
var i = s.Length; // warning CS8602: Dereference of a possibly null reference
}
}
После прочтения документации, на которую я ссылался выше, я ожидал, что s == null
строка выдаст мне предупреждение - в конце концов s
это явно не обнуляемое значение, поэтому сравнивать его null
не имеет смысла.
Вместо этого я получаю предупреждение в следующей строке, и предупреждение говорит, что s
возможна нулевая ссылка, хотя для человека очевидно, что это не так.
Более того, предупреждение не отображается, если мы не сравниваем s
с null
.
Я немного погуглил и столкнулся с проблемой GitHub , которая, как оказалось, была полностью о другом, но в процессе я поговорил с участником, который дал некоторое представление об этом поведении (например, «Нулевые проверки часто являются полезным способом»). сказать компилятору сбросить свой предыдущий вывод об обнуляемости переменной. " ) Однако это все еще оставило меня без ответа на главный вопрос.
Вместо того, чтобы создавать новую проблему на GitHub и потенциально занимать время невероятно занятых участников проекта, я делюсь этим с сообществом.
Не могли бы вы объяснить мне, что происходит и почему? В частности, почему на s == null
линии не генерируются предупреждения , и почему мы имеем, CS8602
когда это не похоже на null
ссылку здесь? Если вывод обнуления не является пуленепробиваемым, как предполагает связанный поток GitHub, как он может пойти не так? Каковы были бы некоторые примеры этого?
источник
?
потому чтоs
это не nullable. Это не становится обнуляемым просто потому, что мы были достаточно глупы, чтобы сравнивать это сnull
.Ответы:
Это фактически дубликат ответа, который связал @stuartd, поэтому я не буду вдаваться в подробности. Но суть в том, что это не ошибка языка и не ошибка компилятора, а предполагаемое поведение в точности как реализовано. Мы отслеживаем нулевое состояние переменной. Когда вы первоначально объявляете переменную, это состояние NotNull, потому что вы явно инициализируете ее значением, которое не является нулевым. Но мы не отслеживаем, откуда появился этот NotNull. Это, например, фактически эквивалентный код:
В обоих случаях, вы явно проверить
s
наnull
. Мы принимаем это как входные данные для анализа потока, так же, как Мадс ответил на этот вопрос: https://stackoverflow.com/a/59328672/2672518 . В результате вы получите предупреждение о возврате. В этом случае ответ заключается в том, что вы получаете предупреждение о том, что вы разыменовали возможную нулевую ссылку.Да, это действительно так. Компилятору. Как люди, мы можем посмотреть на этот код и, очевидно, понять, что он не может генерировать исключение нулевой ссылки. Но способ, которым анализ обнуляемого потока реализован в компиляторе, не может. Мы обсудили некоторые улучшения этого анализа, в которых мы добавили дополнительные состояния в зависимости от того, откуда взялась ценность, но решили, что это значительно усложнило реализацию, не принеся большого выигрыша, потому что единственные места, где это было бы полезно для случаев, подобных этому, когда пользователь инициализирует переменную значением a
new
или константой, а затем проверяет ее вnull
любом случае.источник
s == null
не выдает предупреждение?#nullable enable
;string s = "";s = null;
компилирует и работает (он все еще выдает предупреждение), каковы преимущества реализации, которая позволяет присваивать значение null «ненулевой ссылке» в включенном контексте аннотации null?s == null
. Возможно, вы используете публичный метод и хотите проверить параметры. Или, возможно, вы используете библиотеку, которая аннотировалась неправильно, и до тех пор, пока она не исправит эту ошибку, вам придется иметь дело с нулем, где она не была объявлена. В любом из этих случаев, если мы предупреждали, это был бы плохой опыт. Что касается разрешения присваивания: локальные переменные аннотации только для чтения. Они не влияют на время выполнения вообще. Фактически, мы помещаем все эти предупреждения в один код ошибки, чтобы вы могли отключить их, если хотите уменьшить отток кода.Я с радостью принял обнуляемые ссылки на C # 8, как только они стали доступны. Поскольку я привык использовать нотацию [NotNull] (и т. Д.) В ReSharper, я заметил некоторые различия между ними.
Компилятор C # может быть одурачен, но он склонен ошибаться из-за осторожности (обычно, не всегда).
В качестве справки для будущих посетителей, вот сценарии, о которых я видел, что компилятор был довольно смущен (я предполагаю, что все эти случаи являются разработанными):
Тем не менее, он позволяет использовать некоторую конструкцию для проверки на обнуляемость, которая также избавляет от предупреждения:
или (все еще довольно крутые) атрибуты (найдите их здесь ):
string?
это просто строка, онаint?
становитсяNullable<int>
и вынуждает компилятор обрабатывать их по-разному. Также здесь компилятор выбирает безопасный путь, заставляя вас указать, что вы ожидаете:Решено давать ограничения:
Но если мы не используем ограничения и удаляем '?' Исходя из данных, мы все еще можем поместить в него нулевые значения, используя ключевое слово «по умолчанию»:
Последний вариант кажется мне более сложным, поскольку он позволяет писать небезопасный код.
Надеюсь, это кому-нибудь поможет.
источник
ThrowIfNull(s);
чтоs
оно не является нулевым), не существует. Также в статье объясняется, как обрабатывать ненулевые обобщения, в то время как я показывал, как можно «обмануть» компилятор, имея нулевое значение, но без предупреждения об этом.DoesNotReturnIf(bool)
.DoesNotReturnIfNull(nullable)
.