Почему попытка получить доступ к свойству null вызывает исключение в некоторых языках? - PullRequest
7 голосов
/ 08 августа 2011

В некоторых языках программирования (например, C #, Javascript) меня больше всего беспокоит то, что попытка получить доступ к свойству null вызывает ошибку или исключение.

Например, в следующем фрагменте кода,

foo = bar.baz;

, если bar null, C # выдаст неприятный NullReferenceException, и мой интерпретатор Javascript будет жаловаться на Unable to get value of the property 'baz': object is null or undefined.

Теоретически я могу это понять, но в реальном коде у меня часто бывают более глубокие объекты, такие как

foo.bar.baz.qux

и если любое из значений foo, bar или baz равно нулю, мои коды не работают. :( Кроме того, если я оцениваю следующие выражения в консоли, результаты могут быть противоречивыми:

true.toString() //evaluates to "true"
false.toString() //evaluates to "false"
null.toString() //should evaluate to "null", but interpreter spits in your face instead

Я абсолютно презираю пишу код для решения этой проблемы, потому что это всегда многословный, вонючий код. Следующие примеры не являются надуманными, я взял их из одного из моих проектов (первый в Javascript, второй в C #):

if (!(solvedPuzzles && 
      solvedPuzzles[difficulty] && 
      solvedPuzzles[difficulty][index])) {
      return undefined;
   }
return solvedPuzzles[difficulty][index].star

и

if (context != null &&
   context.Request != null &&
   context.Request.Cookies != null &&
   context.Request.Cookies["SessionID"] != null) 
{
   SessionID = context.Request.Cookies["SessionID"].Value;
}
else
{
   SessionID = null;
}

Все было бы намного проще, если бы все выражение возвращало null, если какое-либо из свойств было бы нулевым. Приведенные выше примеры кода могли бы быть намного проще:

return solvedPuzzles[difficulty][index].star;
    //Will return null if solvedPuzzles, difficulty, index, or star is null.

SessionID = context.Request.Cookies["SessionID"].Value;
    //SessionID will be null if the context, Request, Cookies, 
    //Cookies["SessionID"], or Value is null.

Есть что-то, что я пропускаю? Почему эти языки не используют это поведение вместо ? Это сложно реализовать по какой-то причине? Это вызовет проблемы, которые я пропускаю?

Ответы [ 5 ]

8 голосов
/ 08 августа 2011

Может ли это вызвать проблемы, которые я пропускаю?

Да - это вызовет проблему, при которой вы ожидаете, что будут ненулевыми значениями, ноиз-за ошибки у вас есть нулевое значение.В этой ситуации вы хотите исключение .Сбой в работе и сохранение неверных данных - это действительно плохая идея.

Некоторые языки (например, Groovy) предоставляют нулевой безопасный оператор разыменования, который может помочь.В C # это может выглядеть примерно так:

SessionID = context?.Request?.Cookies?["SessionID"]?.Value;

Я считаю, что команда C # рассмотрела это в прошлом и сочла это проблематичным, но это не значит, что они не будут пересматривать это в будущем, конечно.

2 голосов
/ 08 августа 2011

И потом, если в конце этого был вызов метода, возвращающего bool, то что? Main.Child.Grandchild.IsEdit() и bool IsEdit();

Я думаю, что было бы лучше иметь "нулевой" экземпляр, который возвращает поведение по умолчанию (это известно как шаблон нулевого объекта ).

Таким образом, для тех, кто ожидает, что ноль указывает на проблему, они получают свое исключение, но если вы знаете, что объект по умолчанию приемлем, вы можете реализовать это и не беспокоиться об этом. Оба случая затем раскрываются.

Вы можете получить все "нулевые" объекты из INull, поместить их в хеш против их имени класса, а затем сослаться на это, если член является нулевым. Затем вы можете контролировать реализацию по умолчанию "null" (что было бы приемлемо, если объект имеет значение "null")

Кроме того, «нулевые» объекты могут генерировать исключения, если к ним обращаются, таким образом, вы знаете, что вы получили к ним доступ, и выбираете, когда это будет нормально. Таким образом, вы можете реализовать свое собственное «исключение нулевого объекта».

Если бы они поместили какую-либо языковую поддержку для этого, это было бы либо для всех, по всем направлениям для единицы перевода, либо для объекта за объектом с квалификатором (последний является предпочтительным вариантом), в этот момент это не будет по умолчанию, и вы могли уже реализовать экземпляр по умолчанию "null".

1 голос
/ 08 августа 2011

Могу ли я предположить, что если вам нужно проверить на такое количество нулей, вы плохо структурируете свой код. Очевидно, поскольку у меня нет вашего кода, я не могу сказать это окончательно. Я бы предложил разделить ваш код на более мелкие функции. Таким образом, ваш код должен проверять только один или, возможно, два нуля за раз, и может иметь больший контроль над тем, что происходит, если в какой-то момент что-то имеет значение null.

0 голосов
/ 08 августа 2011

Есть ли что-то, чего мне не хватает?Почему эти языки не используют это поведение вместо этого?Это сложно реализовать по какой-то причине?Могут ли это вызвать проблемы, которые я пропускаю?

Когда переменная равна null, когда не ожидается, что язык, который вы упоминаете, выбирает рано / поздно .Это основная концепция при создании правильно работающих программ.

Можно скрыть проблему с нулевым указателем, и программа запустит еще одну строку, но, возможно, произойдет сбой позже или, что еще хуже, приведет к неправильному выводу!

Также при попытке исправитьошибка, это гораздо проще исправить, если сразу бросить NullReferenceException и отладчик покажет вам строку для начала.Альтернативой могут быть смутные симптомы - гораздо сложнее отлаживать!

Очень часто программировать с null, когда вы не должны.В приведенном выше примере показано, что означает null .Ответ в том, что это ничего не значит.Как читатель кода, я бы предположил, что автор что-то забыл.

Как правило, лучшим решением является введение небольшого, что я называю, «нулевого объекта», который может быть возвращен вместо null.В вашем примере, возможно, класс «NoDifficulty» или «UnknownDifficulty» может быть возвращен для получения результата, которого вы добиваетесь в конце - чистый код.

0 голосов
/ 08 августа 2011

В общем случае foo.Baz означает «в случае, если foo, выполнить элемент baz». Это может быть динамический язык, виртуальный член или что-то еще, но мы потерпели неудачу на первом этапе в предположении: экземпляра нет.

То, что вы помечены, является оператором нулевого доступа к члену. Довольно редко, и, честно говоря, я не согласен с вашим утверждением, что это делает вонючий код (хотя я могу утверждать, что вы запрашиваете слишком много уровней в одном выражении, чтобы быть здоровыми).

Точно так же я не согласен с тем, что null.toString() должен возвращать "ноль" - IMO, это совершенно нормально, если это не получится. Относиться к нулевому доступу как к «нормальному» по своей сути неправильно IMO.

...