Стоит ли проверять на ноль, если он не ожидает ноль?


113

На прошлой неделе у нас был горячий спор об обработке нулей в слое обслуживания нашего приложения. Вопрос в контексте .NET, но он будет таким же в Java и многих других технологиях.

Вопрос заключался в следующем: следует ли вам всегда проверять наличие нулевых значений и заставлять ваш код работать, несмотря ни на что, или позволить исключению всплыть при неожиданном получении нулевого значения?

С одной стороны, проверка на null там, где вы этого не ожидаете (т. Е. У вас нет пользовательского интерфейса для обработки), на мой взгляд, такая же, как написание блока try с пустым catch. Вы просто скрываете ошибку. Ошибка может заключаться в том, что что-то изменилось в коде, и теперь null является ожидаемым значением, или произошла какая-то другая ошибка, и в метод передан неправильный идентификатор.

С другой стороны, проверка на нули может быть хорошей привычкой в ​​целом. Кроме того, если есть проверка, приложение может продолжать работать, только небольшая часть функциональности не оказывает никакого влияния. Затем клиент может сообщить о небольшой ошибке, такой как «невозможно удалить комментарий» вместо гораздо более серьезной ошибки, такой как «невозможно открыть страницу X».

Какую практику вы придерживаетесь и каковы ваши аргументы за или против любого подхода?

Обновить:

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

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

Гипотетическим преимуществом будет то, что страница будет продолжать работать. Подумайте о результатах поиска на Stack Exchange в разных группах (пользователи, комментарии, вопросы). Метод может проверять наличие нуля и прерывать обработку пользователей (которая из-за ошибки равна нулю), но возвращает разделы «комментарии» и «вопросы». Страница продолжит работать, за исключением того, что раздел «пользователи» будет отсутствовать (что является ошибкой). Должны ли мы рано провалиться и сломать всю страницу или продолжить работу и ждать, пока кто-нибудь заметит, что раздел «пользователи» отсутствует?


62
Принцип Фокса Малдера: никому не верь.
Яннис

14
@YannisRizos: этот принцип хорош - он настолько хорош, что создатели Java и C # позволяют языковой среде выполнения делать это автоматически. Поэтому обычно нет необходимости делать явные проверки на нулевое значение везде в вашем коде на этих языках.
Док Браун


Я согласен с ответом tdammers. Я считаю, что проверка на ноль - это просто неправильно, потому что у вас нет разумного способа справиться с этим. Однако в вашем сценарии вы можете обработать сбой раздела, такой как проверка пользователя (или получение комментариев, или что бы то ни было) и наличие поля «о, ошибка не произошла». Я лично регистрирую все исключения в своем приложении и отфильтровываю некоторые исключения, такие как 404, и соединение неожиданно закрывается

2
assert(foo != null, "foo is web control within the repeater, there's no reason to expect it to be null, etc, etc...");
zzzzBov

Ответы:


105

Вопрос не столько в том, следует ли вам проверять nullили позволять среде выполнения генерировать исключение; это то, как вы должны реагировать на такую ​​неожиданную ситуацию.

Ваши варианты тогда:

  • Бросить общее исключение ( NullReferenceException) и позволить ему всплыть; если вы не делаете nullпроверку самостоятельно, это происходит автоматически.
  • Создайте пользовательское исключение, описывающее проблему на более высоком уровне; это может быть достигнуто либо путем добавления nullчека, либо путем перехвата NullReferenceExceptionи выброса более конкретного исключения.
  • Восстановите, подставив подходящее значение по умолчанию.

Не существует общего правила, какое из них является лучшим решением. Лично я бы сказал:

  • Общее исключение лучше всего, если условие ошибки будет признаком серьезной ошибки в вашем коде, то есть значение, которое является нулевым, никогда не должно быть разрешено в первую очередь. Такое исключение может затем полностью переместиться в любую настройку регистрации ошибок, так что кто-то получит уведомление и исправит ошибку.
  • Значение по умолчанию является хорошим решением, если предоставление полу-полезного вывода важнее, чем корректность, например, веб-браузер, который принимает технически некорректный HTML и прилагает все усилия для его разумной визуализации.
  • В противном случае, я бы пошел с конкретным исключением и обработал бы его где-нибудь подходящим.

1
@ Стилгар для конечного пользователя нет разницы. для разработчика конкретное исключение, такое как «сервер базы данных недоступен», более интуитивно понятно, чем «исключение нулевого указателя»
k3b

32
Неудачный и ранний отказ означает, что существует меньший риск того, что что-то пойдет не так, как есть, такие вещи, как неправильная аутентификация, поврежденные данные, сохраненные в хранилище, утечка информации и другие забавные вещи.
Ларс Виклунд

1
@ Стилгар: Здесь есть две проблемы: необслуживаемое поведение и ремонтопригодность. В то время как для поведения без присмотра существует небольшая разница между конкретным исключением и общим, для удобства сопровождения может иметь значение разница между «о, так вот почему все взрывается» и многочасовой дебагатлон.
tdammers

3
тдаммерс абсолютно прав. «Ссылка на объект, не установленная для экземпляра объекта», является одним из самых раздражающих исключений, которые я вижу с наибольшей частотой. Я бы предпочел увидеть конкретное исключение, будь то ArgumentNullException с именем параметра или пользовательское исключение «Нет записи Post для пользователя TK421».
Шон Чейз

1
@ k3b Для конечного пользователя также очень полезен «сервер базы данных недоступен». По крайней мере, для любого конечного пользователя, который имеет некоторое представление о распространенных проблемах - это может помочь ему обнаружить, что кабель не подключен, маршрутизатор нуждается в перезагрузке и т. Д., А не злиться на неисправное программное обеспечение.
Джулия Хейворд

58

ИМХО, попытка обработать нулевые значения, которые вы не ожидаете, приводит к чрезмерно сложному коду. Если вы не ожидаете null, проясните это, бросая ArgumentNullException. Я очень расстраиваюсь, когда люди проверяют, является ли значение нулевым, а затем пытаются написать какой-то код, который не имеет никакого смысла. То же самое относится к использованию SingleOrDefault(или, что еще хуже, к сбору), когда кто-то действительно ожидает, Singleи ко многим другим случаям, когда люди боятся (я не знаю, о чем) четко заявить о своей логике.


Вместо этого ArgumentNullExceptionследует использовать кодовые контракты (если только это не исходный код до 2010 года, который не поддерживает кодовые контракты).
Арсений Мурзенко

Я согласен. Если значение null не ожидается, оно не должно обрабатываться странным образом, а только сообщаться.
Джорджио

9
если я правильно понял вопрос, бросая ArgumentNullExceptionсчет как «обработку» нулевого значения: это предотвращает последующее исключение нулевой ссылки.
KutuluMike

@MichaelEdenfield: Я не думаю, что это считается реальной обработкой в ​​соответствии с заданным вопросом (наша цель - не заставить ваш код работать, несмотря ни на что ). Честно говоря, я часто забываю написать соответствующий блок, который будет выдавать, если пропущен ноль. Тем не менее, я считаю выбрасывание NullArgumentException лучшей практикой, и на мой взгляд, наихудшей практикой является написание кода, который не может обработать нуль, но вместо того, чтобы генерировать исключение, возвращает, например, другой ноль в качестве результата метода (или пустую строку, или пустую коллекцию, или -1 или пропускает выполнение метода, если возвращаемый тип void и т. д.).
empi

2
@ Джорджио, ArgumentNullExceptionочень ясно дает понять, в чем проблема и как ее исправить. C # не склонен использовать «undefined». Если вы называете что-то неопределенным способом, то способ, которым вы называете это, не имеет смысла. Исключением является правильный способ указать это. Теперь иногда я делаю методы синтаксического анализа, которые возвращают ноль, если int(например) невозможно проанализировать. Но это тот случай, когда непарсируемый результат является законной возможностью, а не ошибкой использования. Смотрите также эту статью .
Kyralessa

35

Привычка проверять nullмой опыт исходит от бывших разработчиков на C или C ++, на этих языках у вас есть хороший шанс скрыть серьезную ошибку, когда не проверяете NULL. Как вы знаете, в Java или C # все по-другому, использование нулевого ref без проверки nullприведет к исключению, поэтому ошибка не будет тайно скрыта, если вы не игнорируете ее на вызывающей стороне. Поэтому в большинстве случаев проверка явно nullне имеет большого смысла и усложняет код более, чем необходимо. Вот почему я не считаю нулевую проверку хорошей привычкой в ​​этих языках (по крайней мере, не в целом).

Конечно, есть исключения из этого правила, вот те, о которых я могу думать:

  • Вам нужно более удобное для восприятия человеком сообщение об ошибке, сообщающее пользователю, в какой части программы произошла неправильная нулевая ссылка. Таким образом, ваш тест на нулевое значение выдает просто другое исключение с другим текстом ошибки. Очевидно, что никакая ошибка не будет замаскирована таким образом.

  • Вы хотите, чтобы ваша программа "провалилась раньше". Например, вы хотите проверить нулевое значение параметра конструктора, где в противном случае ссылка на объект будет храниться только в переменной-члене и использоваться позже.

  • Вы можете безопасно справиться с ситуацией нулевого реферирования без риска последующих сбоев и без маскировки серьезной ошибки.

  • вам нужен совершенно другой вид сигнализации об ошибках (например, возвращая код ошибки) в вашем текущем контексте


3
«Вы хотите лучшее, понятное человеку сообщение об ошибке» - это лучшая причина, по моему мнению. «Ссылка на объект не установлена ​​для экземпляра объекта» или «Исключение с нулевой ссылкой» никогда не бывает такой же полезной, как «Продукт не создан».
ПДР

@pdr: Еще одна причина, чтобы проверить это, чтобы убедиться, что он всегда обнаруживается.
Джорджио

13
«Бывшие разработчики C», возможно. «Бывшие разработчики на C ++», только если они сами выздоравливают на C. Как современный разработчик C ++, я бы очень подозрительно относился к коду, который использует голые указатели или безрассудное динамическое размещение. На самом деле, я хотел бы перевернуть аргумент и сказать, что C ++ не страдает от проблемы, описанной в OP, потому что его переменные не имеют дополнительного специального свойства null-ness, которое есть в Java и C #.
Керрек С.Б.

7
Ваш первый абзац - только половина истории: да, если вы используете нулевую ссылку в C #, вы получите исключение, тогда как в C ++ вы получите неопределенное поведение. Но в хорошо написанном C # вы застряли с гораздо более обнуляемыми ссылками, чем в хорошо написанном C ++.
Джеймс МакНеллис

Чем лучше инкапсуляция, тем лучше этот ответ.
радаробоб

15

Использование утверждает , чтобы проверить и указать заранее / постусловия и инварианты для вашего кода.

Это значительно облегчает понимание того, что ожидает код и что с ним можно ожидать.

Утверждение, IMO, является подходящей проверкой, потому что это:

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

Я думаю, что самодокументированный код важен, поэтому иметь чек - это хорошо. Защитное, отказоустойчивое программирование облегчает обслуживание, на которое обычно тратится большинство времени.

Обновить

По поводу вашей разработки. Обычно хорошо дать конечному пользователю приложение, которое хорошо работает при ошибках, то есть показывает как можно больше. Надежность хороша, потому что небеса знают только то, что сломается в критический момент.

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


Утверждения о том, что я не присутствую в релизе, являются для меня большим минусом, релиз - это точное время, когда вы хотите добавить информацию
jk.

1
Что ж, не стесняйтесь использовать условную функцию assert ^ h ^ h ^ h, которая также активна в релизе, если вам это нужно. Я катал свои собственные довольно часто.
Macke

7

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


28
Надеюсь, вы имели в виду: провалиться рано, проваливаться быстро, не часто ...
empi

12
Проблема в том, что без явных проверок нулевое значение может иногда распространяться довольно далеко, прежде чем оно вызовет исключение, и тогда может быть очень трудно выяснить, откуда оно возникло.
Майкл Боргвардт

@MichaelBorgwardt Разве не для этого нужны стековые трассировки?
R0MANARMY

6
@ R0MANARMY: трассировка стека не помогает, когда нулевое значение сохраняется в каком-либо поле, а исключение NullPointerException генерируется намного позже.
Майкл Боргвардт

@ R0MANARMY, даже если вы могли доверять номеру строки в трассировке стека (во многих случаях вы не можете), некоторые строки содержат несколько переменных, которые могут быть нулевыми.
Охад Шнайдер

6
  • Проверка на ноль должна быть вашей второй натурой

  • Ваш API будет использоваться другими людьми, и их ожидания могут отличаться от ваших

  • Тщательные юнит-тесты обычно подчеркивают необходимость нулевой контрольной проверки

  • Проверка на нулевые значения не подразумевает условных операторов. Если вы беспокоитесь о том, что нулевая проверка делает ваш код менее читабельным, вы можете рассмотреть возможность заключения контрактов на код .NET.

  • Попробуйте установить ReSharper (или аналогичный) для поиска в вашем коде пропущенных проверок нулевых ссылок.


Использование кодов контрактов для предварительных условий - это просто прославленные условные выражения.
CVn

Да, я согласен, но я также думаю, что код выглядит чище при использовании контрактов кода, то есть улучшается его читаемость.
CodeART

4

Я хотел бы рассмотреть этот вопрос с точки зрения семантики, т.е. задать себе вопрос, что представляет собой нулевой указатель или нулевой ссылочный аргумент.

Если функция или метод foo () имеет аргумент x типа T, x может быть обязательным или необязательным .

В C ++ вы можете передать обязательный аргумент как

  • Значение, например, void foo (T x)
  • Ссылка, например, void foo (T & x).

В обоих случаях у вас нет проблемы проверки нулевого указателя. Так, во многих случаях вам не нужен нулевой чек указатель на все обязательные аргументы.

Если вы передаете необязательный аргумент, вы можете использовать указатель и использовать значение NULL, чтобы указать, что значение не было задано:

void foo(T* x)

Альтернативой является использование умного указателя, такого как

void foo(shared_ptr<T> x)

В этом случае вы, вероятно, захотите проверить указатель в коде, потому что для метода foo () имеет значение, содержит ли x значение или его нет вообще.

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

  1. Не проверять: если есть ошибка, пусть она вылетает и надеюсь, что эта ошибка появится достаточно рано (во время тестирования). Если этого не произойдет (если вам не удастся провалиться достаточно рано), надейтесь, что он не появится в производственной системе, потому что пользователь увидит сбой приложения.
  2. Проверьте все аргументы в начале метода и сообщите о недопустимых аргументах NULL, например, сгенерируйте исключение из foo () и позвольте другому методу обработать его. Вероятно, лучше всего показать пользователю диалоговое окно с сообщением о том, что приложение обнаружило внутреннюю ошибку и будет закрыто.

Помимо более приятного способа сбоев (предоставления пользователю дополнительной информации), преимущество второго подхода состоит в том, что более вероятно, что ошибка будет обнаружена: программа будет давать сбой каждый раз, когда метод вызывается с неправильными аргументами, тогда как с При подходе 1 ошибка появится только в том случае, если выполняется оператор, разыменовывающий указатель (например, если введена правая ветвь оператора if). Таким образом, подход 2 предлагает более сильную форму «провал рано».

В Java у вас меньше вариантов, потому что все объекты передаются с использованием ссылок, которые всегда могут быть нулевыми. Опять же, если значение NULL представляет значение NONE необязательного аргумента, то проверка значения NULL (вероятно) будет частью логики реализации.

Если значение NULL является недопустимым, у вас есть ошибка, и я бы применил те же соображения, что и в случае указателей C ++. Поскольку в Java вы можете перехватывать исключения нулевого указателя, описанный выше подход 1 эквивалентен подходу 2, если метод foo () разыменовывает все входные аргументы во всех путях выполнения (что, вероятно, происходит очень часто для небольших методов).

подведение

  • Как в C ++, так и в Java, проверка наличия нулевых необязательных аргументов является частью логики программы.
  • В C ++ я всегда проверял бы обязательные аргументы, чтобы убедиться, что выброшено правильное исключение, чтобы программа могла обрабатывать его надежным способом.
  • В Java (или C #) я бы проверил обязательные аргументы, если есть пути выполнения, которые не будут выбрасываться, чтобы иметь более сильную форму «провал рано».

РЕДАКТИРОВАТЬ

Спасибо Стилгару за более подробную информацию.

В вашем случае кажется, что у вас есть null как результат метода. Опять же, я думаю, вы должны сначала уточнить (и исправить) семантику ваших методов, прежде чем вы сможете принять решение.

Итак, у вас есть метод m1 (), вызывающий метод m2 (), и m2 () возвращает ссылку (в Java) или указатель (в C ++) на некоторый объект.

Какова семантика m2 ()? Должен ли m2 () всегда возвращать ненулевой результат? Если это так, то нулевой результат является внутренней ошибкой (в м2 ()). Если вы проверите возвращаемое значение в m1 (), вы можете иметь более надежную обработку ошибок. Если вы этого не сделаете, у вас, вероятно, будет исключение, рано или поздно. Возможно, нет. Надеюсь, что исключение выдается во время тестирования, а не после развертывания. Вопрос : если m2 () никогда не должен возвращать ноль, почему он возвращает ноль? Может быть, вместо этого следует выдать исключение? m2 () вероятно глючит.

Альтернативой является то, что возвращение null является частью семантики метода m2 (), то есть имеет необязательный результат, который должен быть задокументирован в документации Javadoc метода. В этом случае все в порядке, чтобы иметь нулевое значение. Метод m1 () должен проверить его как любое другое значение: здесь нет ошибки, но, вероятно, проверка, является ли результат нулевым, является лишь частью логики программы.


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

Таким образом, результат, возвращаемый методом, был ссылкой на объект, а null был значением, используемым для представления результата: NOT FOUND. Я правильно понимаю?
Джорджио

Я обновил вопрос с дополнительными деталями.
Стилгар

3

Мой общий подход заключается в том, чтобы никогда не проверять состояние ошибки, с которым вы не знаете, как справиться . Тогда возникает вопрос, на который нужно ответить в каждом конкретном случае: можете ли вы сделать что-то разумное в присутствии неожиданного nullзначения?

Если вы можете продолжить безопасно, подставив другое значение (скажем, пустую коллекцию, или значение или экземпляр по умолчанию), то обязательно сделайте это. Пример @ Shahbaz из World of Warcraft о замене одного изображения другим попадает в эту категорию; само изображение, скорее всего, просто украшение, и не оказывает никакого функционального воздействия. (В отладочной сборке я бы использовал кричащий цвет, чтобы привлечь внимание к тому факту, что произошла замена, но это сильно зависит от природы приложения.) Тем не менее, регистрируйте подробности об ошибке, чтобы вы знали, что она происходит; в противном случае вы будете скрывать ошибку, о которой, вероятно, хотите знать. Скрывать это от пользователя - это одно (опять же, при условии, что обработка может продолжаться безопасно); скрывать это от разработчика - совсем другое.

Если вы не можете продолжить безопасно при наличии нулевого значения, то произойдет сбой с явной ошибкой; в общем, я предпочитаю потерпеть неудачу как можно скорее, когда очевидно, что операция не может быть успешной, что подразумевает проверку предварительных условий. Бывают моменты, когда вы не хотите сразу же потерпеть неудачу, а откладываете неудачу как можно дольше; Это соображение возникает, например, в приложениях, связанных с криптографией, где атаки по побочным каналам могут в противном случае разгласить информацию, которую вы не хотите знать. В конце позвольте ошибке всплыть в некотором общем обработчике ошибок, который, в свою очередь, регистрирует соответствующие подробности и отображает приятное (как бы это ни было приятно) сообщение об ошибке для пользователя.

Также стоит отметить, что правильный ответ может очень сильно отличаться между сборками тестирования / QA / отладки и сборками выпуска / выпуска. В отладочных сборках я бы пошел на ранние и серьезные сбои с очень подробными сообщениями об ошибках, чтобы было совершенно очевидно, что есть проблема, и позволить после вскрытия определить, что нужно сделать, чтобы ее исправить. Производственные сборки, когда они сталкиваются с безопасными восстанавливаемыми ошибками, вероятно, должны способствовать восстановлению.


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

@ Стилгар, если некому проверить журналы, то это не проблема с наличием или отсутствием нулевых проверок.
CVn

2
Существует проблема. Конечные пользователи могут не заметить, что система долго не работает правильно, если есть нулевые проверки, которые просто скрывают ошибку и записывают ее в журналы.
Стилгар

@ Стилгар, это полностью зависит от характера ошибки. Как я сказал в посте, только если вы можете продолжать безопасно, неожиданный ноль будет заменен / проигнорирован. Использование одного образа вместо другого в сборке выпуска (в отладочных сборках происходит сбой как можно раньше и настолько сложно, насколько это возможно, чтобы проблема стала очевидной) - это совсем другое дело, чем, например, возвращать неверные результаты для расчета. (Отредактированный ответ, чтобы включить это.)
CVn

1
Я бы пошел с ошибками быстро и рано даже на производстве, если вы не можете каким-то образом войти в систему и сообщить об ошибке (не слишком полагаясь на опытных пользователей). Скрывать ваши развернутые ошибки от пользователей - это одно: скрывать их от себя опасно. Кроме того, позволяя пользователю жить с тонкими ошибками, можно подорвать их доверие к вашему приложению. Иногда лучше укусить пулю и сообщить им, что была ошибка, и вы быстро ее исправили.
Мерлин Морган-Грэм

2

Конечно NULL, не ожидается , но всегда есть ошибки!

Я считаю, что вы должны проверить NULL, не ожидаете ли вы этого. Позвольте мне привести вам примеры (хотя они не на Java).

  • В World of Warcraft из-за ошибки изображение одного из разработчиков появилось на кубе для многих объектов. Представьте, что если бы графический движок не ожидал NULL указателей, произошел бы сбой, в то время как он превратился в не столь фатальный (даже забавный) опыт для пользователя. Самое меньшее, вы бы не потеряли соединение или точки сохранения.

    Это пример, когда проверка на NULL предотвратила сбой, и случай был обработан без уведомления.

  • Представьте себе программу кассира банка. Интерфейс выполняет некоторые проверки и отправляет входные данные в базовую часть для выполнения денежных переводов. Если основная часть ожидает, что не получит NULL, то ошибка в интерфейсе может вызвать проблему в транзакции, возможно, некоторые деньги в середине теряются (или, что еще хуже, дублируются).

    Под «проблемой» я имею в виду сбой (как в случае сбоя (скажем, программа написана на C ++), а не исключение нулевого указателя). В этом случае транзакция должна откатиться, если встретится NULL (что, конечно, было бы невозможно, если бы вы не проверяли переменные на NULL!)

Я уверен, что вы поняли идею.

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

Если вам нужно выбрать между «программа сообщает пользователю, что произошел сбой, и она корректно завершает работу или откатывается до согласованного состояния, возможно создавая журнал ошибок» и «Нарушение прав доступа», разве вы не выбрали бы первое? Это означает, что каждая функция должна быть подготовлена ​​для вызова с помощью глючного кода, а не ожидать, что все правильно, просто потому, что ошибки существуют всегда.


Ваш второй пример опровергает вашу точку зрения. В банковской транзакции, если что-то пойдет не так, транзакция должна быть прервана. Именно когда вы скрываете ошибку, деньги могут быть потеряны или дублированы. Проверка на нулевое значение будет выглядеть так: «Клиент отправляет нулевые деньги ... ну, я просто отправлю 10 долларов (эквивалент имиджа разработчика)»
Stilgar

@ Стилгар, наоборот! Проверка на NULL будет прервана с сообщением. Не проверка на NULL приведет к сбою . Теперь сбой в начале транзакции может быть не плохим, а в середине может быть катастрофическим. (Я не говорил, что банковский перевод должен обрабатывать ошибку, как в игре! Просто тот факт, что он должен ее обрабатывать)
Шахбаз

2
Смысл «транзакции» в том, что она не может быть завершена наполовину :) Если есть ошибка, то она откатывается.
Стилгар

Это ужасный совет для всего, где фактически требуется транзакционная семантика. Вы должны сделать все свои транзакции атомарными и идемпотентными, в противном случае любая ваша ошибка будет гарантировать потерю или дублирование ресурсов (денег). Если вам приходится иметь дело с неатомарными или неидемпотентными операциями, вы должны очень тщательно обернуть их в слои, которые гарантируют атомарное и идемпотентное поведение (даже если вы сами пишете эти слои). Всегда думайте: что произойдет, если метеор попадет на веб-сервер в момент совершения транзакции?
Мерлин Морган-Грэм

1
@ MerlynMorgan-Грэм, я так и сделал. Надеюсь, что это устранит путаницу.
Шахбаз

2

Как указано в других ответах. Если вы не ожидаете NULL, проясните это, бросая ArgumentNullException. На мой взгляд, когда вы отлаживаете проект, это помогает вам быстрее обнаружить ошибки в логике вашей программы.

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

Другой момент заключается в том, что если NULLпроверка выполняется для параметров функции, то вы можете решить, является ли функция подходящим местом для этого или нет; если нет, найдите все ссылки на функцию, а затем перед передачей параметра в функцию просмотрите возможные сценарии, которые могут привести к нулевой ссылке.


1

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


0

Хотя исключение нулевого указателя в конечном итоге будет выдано, оно часто будет отбрасываться далеко от точки, в которой вы получили нулевой указатель. Сделайте себе одолжение и сократите время, необходимое для исправления ошибки, по крайней мере, регистрируя тот факт, что вы получаете нулевой указатель как можно скорее.

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


-1

Поскольку, Fail early, fail fastкак сказано в @DeadMG (@empi), это невозможно, так как веб-приложение должно хорошо работать при ошибках, вы должны применить правило

нет ловли исключений без регистрации

так что вы, как разработчики, знаете о потенциальных проблемах, просматривая журналы.

Если вы используете каркасы журналирования, такие как log4net или log4j, вы можете настроить дополнительный специальный вывод журналирования (он же appender), который будет отправлять вам сообщения об ошибках и ошибках. так что вы в курсе .

[Обновить]

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

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

Независимо от того, как вы используете ведение журнала, если вы проглотили исключение, программа продолжит работу с данными, которые имеют смысл, и проглатывание больше не будет молчать.


1
Вопрос в том, что происходит со страницей?
Стилгар

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

«Поскольку ранний сбой, быстрый провал, как указано @DeadMG (@empi), невозможен, потому что веб-приложение должно хорошо работать при ошибках, вы должны применять правило»: всегда можно перехватить все исключения (catch (Throwable t)) и перенаправить на страницу с ошибкой. Разве это не возможно?
Джорджио

-1

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

Утверждения в такой ситуации позволят любому модульному и / или интеграционному тесту провалиться с точным сообщением, так что вы можете довольно быстро решить проблему до начала производства. Если такая ошибка проскальзывает через тесты и поступает в производство (где утверждения должны быть деактивированы), вы получите NpEx и серьезную ошибку, но это лучше, чем какая-то незначительная ошибка, которая гораздо более нечеткая и появляется где-то еще в код, и было бы дороже, чтобы исправить.

Более того, если кто-то должен работать с вашими утверждениями в коде, он расскажет ему о ваших предположениях, которые вы сделали во время разработки / написания кода (в данном случае: Привет, чувак, этот объект должен быть представлен!), Что облегчает поддержку.


Не могли бы вы написать что-нибудь для голосования? Буду признателен за обсуждение. Спасибо
GeT

Я не голосовал против, и я согласен с вашей общей идеей. Язык нуждается в некоторой работе, хотя. Ваши утверждения определяют контракт вашего API, и вы быстро проваливаетесь / быстро терпите неудачу, если эти контракты нарушаются. Выполнение этого в оболочке вашего приложения позволит пользователям вашего кода знать, что они сделали что-то не так в своем коде. Если они увидят трассировку стека в углублениях вашего кода, они, скорее всего, сочтут, что ваш код содержит ошибки - и вы тоже можете думать об этом, пока не получите надежное воспроизведение и отладку.
Мерлин Морган-Грэм

-1

Обычно я проверяю наличие нулевых значений, но я «обрабатываю» неожиданные нулевые значения, выдавая исключение, которое дает немного больше информации о том, где именно произошло нулевое значение.

Изменить: Если вы получаете ноль, когда вы не ожидаете, что что-то не так - программа должна умереть.


-1

Это зависит от языковой реализации исключений. Исключения позволяют вам предпринять некоторые действия, чтобы предотвратить повреждение данных или аккуратно высвободить ресурсы, если ваша система войдет в непредвиденное состояние или состояние. Это отличается от обработки ошибок, которая является условием, которое вы можете ожидать. Моя философия заключается в том, что у вас должно быть как можно меньше обработки исключений. Это шум. Может ли ваш метод сделать что-то разумное, обработав исключение? Это может восстановиться? Это может восстановить или предотвратить дальнейшее повреждение? Если вы просто собираетесь поймать исключение, а затем вернетесь из метода или ошиблись каким-либо другим безобидным способом, то поймать исключение было бессмысленно. Вы бы только добавили шум и сложность в свой метод, и вы можете скрывать источник ошибки в вашем дизайне. В этом случае я бы позволил ошибке всплыть и пойматься внешней петлей. Если вы можете сделать что-то, например, восстановить объект или пометить его как поврежденный или освободить какой-либо критически важный ресурс, такой как файл блокировки, или просто закрыв сокет, тогда это хорошее использование исключений. Если вы действительно ожидаете, что NULL будет часто отображаться как допустимый крайний случай, то вы должны обрабатывать это в обычном логическом потоке с помощью операторов if-the-else или switch-case или чего-либо еще, а не с обработкой исключений. Например, поле, отключенное в форме, может иметь значение NULL, которое отличается от пустой строки, представляющей поле, оставленное пустым. Это та область, где вы должны использовать здравый смысл и здравый смысл. Я никогда не слышал о хороших эмпирических правилах о том, как справиться с этой проблемой в любой ситуации. Если вы можете сделать что-то, например, восстановить объект или пометить его как поврежденный или освободить какой-либо критически важный ресурс, такой как файл блокировки, или просто закрыв сокет, тогда это хорошее использование исключений. Если вы действительно ожидаете, что NULL будет часто отображаться как допустимый крайний случай, то вы должны обрабатывать это в обычном логическом потоке с помощью операторов if-the-else или switch-case или чего-либо еще, а не с обработкой исключений. Например, поле, отключенное в форме, может иметь значение NULL, которое отличается от пустой строки, представляющей поле, оставленное пустым. Это та область, где вы должны использовать здравый смысл и здравый смысл. Я никогда не слышал о хороших эмпирических правилах о том, как справиться с этой проблемой в любой ситуации. Если вы можете сделать что-то, например, восстановить объект или пометить его как поврежденный или освободить какой-либо критически важный ресурс, такой как файл блокировки, или просто закрыв сокет, тогда это хорошее использование исключений. Если вы действительно ожидаете, что NULL будет часто отображаться как допустимый крайний случай, то вы должны обрабатывать это в обычном логическом потоке с помощью операторов if-the-else или switch-case или чего-либо еще, а не с обработкой исключений. Например, поле, отключенное в форме, может иметь значение NULL, которое отличается от пустой строки, представляющей поле, оставленное пустым. Это та область, где вы должны использовать здравый смысл и здравый смысл. Я никогда не слышал о хороших эмпирических правилах о том, как справиться с этой проблемой в любой ситуации.

Java терпит неудачу в своей реализации обработки исключений с «проверенными исключениями», где каждое возможное исключение, которое может быть вызвано объектами, используемыми в методе, должно быть перехвачено или объявлено в предложении «throws» метода. Это противоположность C ++ и Python, где вы можете обрабатывать исключения по своему усмотрению. В Java, если вы модифицируете тело метода для включения вызова, который может вызвать исключение, вы сталкиваетесь с выбором добавления явной обработки для исключения, которое может вас не волновать, таким образом добавляя шум и сложность вашему коду, или вы должны добавьте это исключение к условию throws модифицируемого вами метода, который не только добавляет шум и беспорядок, но и меняет сигнатуру вашего метода. Затем вы должны перейти к коду модификации, где бы ни использовался ваш метод, для обработки нового исключения, которое ваш метод теперь может вызвать, или добавить исключение к предложению «throws» этих методов, вызывая эффект домино изменений кода. «Поймать или указать требование» должно быть одним из самых раздражающих аспектов Java. Исключение исключения для RuntimeExceptions и официальное объяснение Java для этой ошибки проектирования - хромое.

Последний абзац имел мало общего с ответом на ваш вопрос. Я просто ненавижу Java.

- Ной


этот пост довольно трудно читать (стена текста). Не могли бы вы изменить его в лучшую форму?
комнат
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.