Так что я не знаю, хороший это или плохой дизайн кода, поэтому я подумал, что лучше спросить.
Я часто создаю методы, которые выполняют обработку данных с использованием классов, и я часто проверяю методы, чтобы убедиться, что я не получаю нулевые ссылки или другие ошибки заранее.
Для очень простого примера:
// fields and properties
private Entity _someEntity;
public Entity SomeEntity => _someEntity;
public void AssignEntity(Entity entity){
_someEntity = entity;
}
public void SetName(string name)
{
if (_someEntity == null) return; //check to avoid null ref
_someEntity.Name = name;
label.SetText(_someEntity.Name);
}
Так что, как вы можете видеть, я проверяю нулевое значение каждый раз. Но должен ли метод не иметь эту проверку?
Например, внешний код должен очищать данные перед использованием, чтобы методы не проверялись, как показано ниже:
if(entity != null) // this makes the null checks redundant in the methods
{
Manager.AssignEntity(entity);
Manager.SetName("Test");
}
Таким образом, если методы «проверяют данные», а затем выполняют их обработку данных, или это должно быть гарантировано перед вызовом метода, и если вам не удалось проверить перед вызовом метода, он должен выдать ошибку (или перехватить ошибка)?
источник
Ответы:
Проблема с вашим базовым примером не в нулевой проверке, а в тихом сбое .
Ошибки нулевого указателя / ссылки чаще всего являются ошибками программиста. Ошибки программиста часто лучше всего устраняются путем немедленного и громкого сбоя. У вас есть три основных способа решения проблемы в этой ситуации:
Третье решение - больше работы, но гораздо надежнее:
_someEntity
никогда не был в недопустимом состоянии. Один из способов сделать это - избавиться от негоAssignEntity
и потребовать его в качестве параметра для создания экземпляра. Другие методы внедрения зависимости также могут быть полезны.В некоторых ситуациях имеет смысл проверить правильность всех аргументов функции перед любой работой, которую вы делаете, а в других имеет смысл сообщить вызывающей стороне, что они несут ответственность за достоверность своих входных данных, а не проверку. На каком конце спектра вы находитесь, зависит от вашей проблемной области. Третий вариант имеет существенное преимущество в том, что вы можете, в некоторой степени, заставить компилятор принудительно выполнять все действия вызывающей стороны.
Если третий вариант не вариант, то, на мой взгляд, это действительно не имеет значения, пока он не потерпит неудачу. Если не проверка на нулевое значение приводит к мгновенному взлому программы, хорошо, но если вместо этого она незаметно повреждает данные, чтобы вызвать проблемы в будущем, тогда лучше всего проверить и разобраться с этим прямо сейчас и там.
источник
null
является ли он неправильным или недействительным, потому что в моей гипотетической библиотеке я определил типы данных и определил, что является действительным, а что нет. Вы называете это «принуждением к предположениям», но угадайте, что: это то, что делает каждая существующая библиотека программного обеспечения. Есть моменты, когда null является допустимым значением, но это не «всегда».null
должным образом» - это неправильное понимание того, что означает правильная обработка. Для большинства Java-программистов это означает создание исключения для любого неверного ввода. Используя@ParametersAreNonnullByDefault
, это означает также для каждогоnull
, если аргумент не помечен как@Nullable
. Делать что-либо еще означает удлинять путь до тех пор, пока он, наконец, не уйдет в другое место, и, таким образом, также увеличить время, потраченное на отладку. Что ж, игнорируя проблемы, вы можете достичь того, что это никогда не случится, но неправильное поведение тогда довольно гарантировано.Exception
являются совершенно не связанными дебатами. (Я согласен, что оба хлопотны.) Для этого конкретного примера здесь,null
очевидно, нет смысла, если целью класса является вызов методов для объекта.Поскольку
_someEntity
его можно изменить на любом этапе, имеет смысл проверять его каждый раз, когдаSetName
вызывается. В конце концов, это могло измениться с момента последнего вызова этого метода. Но имейте в виду, что кодSetName
не является потокобезопасным, поэтому вы можете выполнить эту проверку в одном потоке,_someEntity
установить вnull
другой, и тогда код всеNullReferenceException
равно будет заблокирован .Поэтому один из подходов к этой проблеме - стать еще более оборонительным и выполнить одно из следующих действий:
_someEntity
и ссылку на эту копию с помощью метода,_someEntity
чтобы убедиться, что она не изменяется при выполнении метода,try/catch
и выполняйте те же действия с любымиNullReferenceException
объектами, которые встречаются в методе, что вы выполняете при первоначальной проверке нуля (в данном случаеnop
действие).Но что вам действительно нужно сделать, так это остановиться и задать себе вопрос: нужно ли вам на самом деле разрешить
_someEntity
перезапись? Почему бы не установить его один раз, через конструктор. Таким образом, вам нужно всего лишь сделать эту нулевую проверку один раз:Если это вообще возможно, я бы рекомендовал такой подход в таких ситуациях. Если вы не можете помнить о безопасности потоков при выборе способа обработки возможных нулей.
В качестве бонуса я чувствую, что ваш код странный и может быть улучшен. Все:
можно заменить на:
Имеет ту же функциональность, за исключением возможности устанавливать поле через установщик свойств, а не метод с другим именем.
источник
Это твой выбор.
Создавая
public
метод, вы предлагаете общественности возможность его вызвать. Это всегда сопровождается неявным договором о том, как вызывать этот метод и что ожидать при этом. Этот контракт может (или не может) включать « да, хм, если вы передадитеnull
в качестве значения параметра, он взорвется прямо на вашем лице ». Другие части этого контракта могут быть « о, кстати: каждый раз, когда два метода выполняют этот метод одновременно, котенок умирает ».Какой тип контракта вы хотите разработать, зависит от вас. Важные вещи должны
создать API, который является полезным и последовательным и
хорошо документировать это, особенно для тех крайних случаев.
Каковы вероятные случаи, когда ваш метод вызывается?
источник
Другие ответы хороши; Я хотел бы расширить их, сделав несколько комментариев о модификаторах доступности. Во-первых, что делать, если
null
никогда не действует:публичные и защищенные методы - это те, где вы не контролируете вызывающего. Они должны бросить, когда прошло ноль. Таким образом вы научите своих абонентов никогда не пропускать вас в ноль, потому что они хотят, чтобы их программа не вылетала.
внутренние и частные методы являются те , где вы действительно контролировать абонент. Они должны утверждать, когда передано нуль; потом они, вероятно, вылетят позже, но, по крайней мере, вы сначала получили утверждение и возможность взломать отладчик. Опять же, вы хотите научить своих абонентов правильно звонить вам, делая это больно, когда они этого не делают.
Теперь, что вы делаете, если это может быть допустимым для передачи нулевого значения? Я бы избежал этой ситуации с шаблоном нулевого объекта . То есть: создайте специальный экземпляр типа и потребуйте, чтобы он использовался каждый раз, когда требуется недопустимый объект . Теперь вы снова в приятном мире бросания / утверждения при каждом использовании нуля.
Например, вы не хотите быть в такой ситуации:
и на сайте вызова:
Потому что (1) вы обучаете своих вызывающих, что null является хорошим значением, и (2) неясно, какова семантика этого значения. Скорее сделайте это:
И теперь сайт вызова выглядит так:
и теперь читатель кода знает, что это значит.
источник
if
s для нулевых объектов, а используйте полиморфизм, чтобы заставить их вести себя правильно.EmptyQueue
тип, который выбрасывает при удалении из очереди, и так далее.Person
класс был неизменным и не содержал конструктор с нулевым аргументом, как бы вы сконструировали вашиApproverNotRequired
илиApproverUnknown
экземпляры? Другими словами, какие значения вы бы передали конструктору?Другие ответы указывают, что ваш код может быть очищен, чтобы не нуждаться в нулевой проверке, где он у вас есть, однако, для общего ответа о том, что полезная нулевая проверка может быть для рассмотрения следующего примера кода:
Это дает вам преимущество в обслуживании. Если в вашем коде когда-либо возникает дефект, когда что-то передает нулевой объект методу, вы получите полезную информацию от метода относительно того, какой параметр был нулевым. Без проверок вы получите NullReferenceException в строке:
И (учитывая, что свойство Something является типом значения), a или b могут иметь значение null, и вы не сможете узнать, какой из трассировки стека. С помощью проверок вы будете точно знать, какой объект был нулевым, что может быть чрезвычайно полезно при попытке выявить дефект.
Я хотел бы отметить, что шаблон в вашем исходном коде:
Может использоваться в определенных обстоятельствах, а также (возможно, чаще) дает вам очень хороший способ маскировки основных проблем в вашей кодовой базе.
источник
Нет, совсем нет, но это зависит.
Вы только делаете нулевую проверку ссылки, если хотите реагировать на это.
Если вы выполняете проверку нулевой ссылки и генерируете проверенное исключение , вы заставляете пользователя метода реагировать на него и позволяете ему восстановиться. Программа по-прежнему работает, как и ожидалось.
Если вы не выполните проверку нулевой ссылки (или сгенерируете непроверенное исключение), он может выдать непроверенную проверку
NullReferenceException
, которая обычно не обрабатывается пользователем метода и даже может завершить работу приложения. Это означает, что функция, очевидно, полностью неправильно понята и, следовательно, приложение неисправно.источник
Что-то вроде расширения ответа @ null, но, по моему опыту, делать нулевые проверки, несмотря ни на что.
Хорошее защитное программирование - это позиция, при которой вы кодируете текущую подпрограмму изолированно, исходя из того, что вся остальная часть программы пытается ее остановить. Когда вы пишете критически важный код, где сбой не возможен, это практически единственный путь.
Теперь, как вы на самом деле имеете дело со
null
значением, это во многом зависит от вашего варианта использования.Если
null
это допустимое значение, например, любой из параметров со второго по пятый для подпрограммы MSVC _splitpath (), то правильное поведение в нем.Если
null
этого не произойдет при вызове рассматриваемой подпрограммы, т. Е. В случае OP, контракт API требует,AssignEntity()
чтобы он был вызван с допустимым значением, аEntity
затем выдает исключение или иным образом завершается неудачей, гарантируя, что вы создадите много шума в процессе.Никогда не беспокойтесь о стоимости нулевой проверки, они очень дешевы, если они случаются, и с современными компиляторами статический анализ кода может и полностью исключит их, если компилятор определит, что этого не произойдет.
источник