Должен ли я убедиться, что аргументы не равны нулю, прежде чем использовать их в функции?

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

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

Public Shared Function GenerateHash(ByVal FilePath As IO.FileInfo) As String
        If (FilePath Is Nothing) Then
            Throw New ArgumentNullException("FilePath")
        End If

        Dim _sha As New Security.Cryptography.MD5CryptoServiceProvider
        Dim _Hash = Convert.ToBase64String(_sha.ComputeHash(New IO.FileStream(FilePath.FullName, IO.FileMode.Open, IO.FileAccess.Read)))
        Return _Hash
    End Function

Как вы можете видеть, я просто беру IO.Fileinfo в качестве аргумента в начале функции, которую проверяю, чтобы убедиться, что это не пустяк.

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

Спасибо.

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Валидация полей ввода для базовой формы React
Валидация полей ввода для базовой формы React
В одном из моих проектов MERN Stack есть форма с именем, фамилией, контактным номером, адресом, электронной почтой, датой рождения, номером NIC, весом...
Пользовательские правила валидации в Laravel
Пользовательские правила валидации в Laravel
Если вы хотите создать свое собственное правило валидации, Laravel предоставляет возможность сделать это. Создайте правило с помощью следующей...
6
0
1 038
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Если NULL является недопустимым вводом, вызовите исключение. Самостоятельно, как в своем примере, так что сообщение будет полезным.

Другой метод обработки входных данных NULL - это просто по очереди откликнуться на NULL. Зависит от типа функции - в приведенном выше примере я оставил бы исключение.

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

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

Ответ принят как подходящий

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

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

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

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

Однако, если вы можете что-то добавить к нему, не повредит обернуть исключение более точным и повторно выбросить его. Декодирование «NullPointerException» займет немного больше времени, чем «IllegalArgumentException (« FilePath ДОЛЖЕН быть предоставлен »)» (или что-то еще).

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

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

Да, рекомендуется проверять все аргументы в начале метода и генерировать соответствующие исключения, такие как ArgumentException, ArgumentNullException или ArgumentOutOfRangeException.

Если метод является частным, так что только вы, программист, можете передавать недопустимые аргументы, вы можете выбрать утверждение, что каждый аргумент действителен (Debug.Assert) вместо throw.

Один из моих любимых приемов в C++ - DEBUG_ASSERT для указателей NULL. Это было внушено мне старшими программистами (наряду с правильностью констант), и это одна из вещей, к которой я был наиболее строг при проверке кода. Мы никогда разыменовали указатель без предварительного утверждения, что он не был нулевым.

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

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

Если вы пишете общедоступный API, сделайте своему вызывающему абоненту услугу, помогите ему быстро найти свои ошибки и проверьте правильность ввода.

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

Если ваши API доступны только для доверенных вызывающих, например «internal» в C#, тогда не думайте, что вам нужно писать весь этот дополнительный код. Это никому не пригодится.

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

Как и в вашем примере, если нулевой аргумент вашей функции не имеет никакого смысла, и вы предполагаете, что любой, кто использует вашу функцию, будет знать об этом, то передача нулевого аргумента показывает какую-то ошибку и какое-то действие (например, .выбрасывает исключение). И если вы используете утверждения (как Джеймс Фассетт сказал передо мной ;-)), они вам ничего не будут стоить в окончательной версии. (они почти ничего не стоят и в отладочной версии)

То же самое относится и к любому другому предположению.

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

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

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

Классический дизайн по контракту: если ввод правильный, то и вывод будет правильным. Если ввод неверен, это ошибка. (если ввод правильный, а вывод - неправильный, есть ошибка. Дайте мне знать.)

и если ввод неправильный, а вывод правильный ... хм .. это тоже не очень хорошо.

stephenbayer 14.10.2008 07:10

Я добавлю пару уточнений (жирным шрифтом) к отличному дизайну по совету по контракту, предложенному ранее Брайаном ...

Принципы «проектирования по контракту» требуют, чтобы вы определили, что допустимо для передачи вызывающим (допустимый домен входных значений), а затем, для любого допустимого ввода, что будет делать метод / поставщик.

Для внутреннего метода вы можете определить значения NULL как вне области допустимых входных параметров. В этом случае вы должны немедленно заявить, что значение входного параметра НЕ НУЛЕВО. Ключевым моментом в этой спецификации контракта является то, что любой вызов, передающий значение NULL ЯВЛЯЕТСЯ ОШИБКОЙ ВЫЗОВА и ошибка, выдаваемая оператором assert, является правильным поведением.

Теперь, несмотря на то, что он очень хорошо определен и экономичен, если вы предоставляете метод внешним / общедоступным вызывающим объектам, вы должны спросить себя, действительно ли это тот контракт, который я / мы действительно хотим? Возможно нет. В общедоступном интерфейсе вы, вероятно, примете NULL (как технически в области входных данных, которые принимает метод), но затем откажетесь от корректной обработки с ответным сообщением. (Больше работы, чтобы удовлетворить естественно более сложные требования, предъявляемые к клиентам.)

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

Другие вопросы по теме