Я большой поклонник написания заявлений, контрактов или любых других типов проверок, доступных на языке, который я использую. Одна вещь, которая меня немного беспокоит, это то, что я не уверен, какова общая практика работы с дублирующимися чеками.
Пример ситуации: сначала я пишу следующую функцию
void DoSomething( object obj )
{
Contract.Requires<ArgumentNullException>( obj != null );
//code using obj
}
затем через несколько часов я пишу другую функцию, которая вызывает первую. Поскольку все еще свежо в памяти, я решил не дублировать контракт, так как я знаю, что уже DoSomething
проверит нулевой объект:
void DoSomethingElse( object obj )
{
//no Requires here: DoSomething will do that already
DoSomething( obj );
//code using obj
}
Очевидная проблема: DoSomethingElse
теперь зависит от DoSomething
проверки того, что obj не является нулевым. Так что DoSomething
когда-нибудь решится больше не проверять, или если я решу использовать другую функцию, obj может больше не проверяться. Что приводит меня к написанию этой реализации в конце концов:
void DoSomethingElse( object obj )
{
Contract.Requires<ArgumentNullException>( obj != null );
DoSomething( obj );
//code using obj
}
Всегда в безопасности, не беспокойтесь, за исключением того, что если ситуация растет, один и тот же объект может быть проверен несколько раз, и это является формой дублирования, и мы все знаем, что это не так хорошо.
Какова наиболее распространенная практика для подобных ситуаций?
источник
ArgumentBullException
? Это новый :)Ответы:
Лично я бы проверил на нулевое значение в любой функции, которая потерпит неудачу, если она получает нулевое значение, а не в любой функции, которая не будет.
Итак, в приведенном выше примере, если doSomethingElse () не нужно разыменовывать obj, я бы не проверял obj на наличие нулевого значения.
Если DoSomething () разыменовывает obj, тогда он должен проверять наличие нуля.
Если обе функции разыменовывают его, они оба должны проверить. Так что, если DoSomethingElse разыменовывает obj, тогда он должен проверять на ноль, но DoSomething также должен проверять на ноль, так как он может быть вызван из другого пути.
Таким образом, вы можете оставить код достаточно чистым и при этом гарантировать, что проверки находятся в правильном месте.
источник
DoSomething()
, что предварительное условие больше не требуется (маловероятно в данном конкретном случае, но может произойти в другой ситуации), и удалите проверку предварительного условия. Теперь какой-то, казалось бы, совершенно не связанный метод сломан из-за отсутствия предварительного условия. Я возьму немного дублирования кода для ясности по поводу странных сбоев, подобных этому, из желания сохранить несколько строк кода в любой день.Большой! Я вижу, вы узнали о кодексах для .NET. Контракты на кодирование идут намного дальше, чем ваши средние утверждения, лучшим примером которых является статическая проверка . Это может быть недоступно для вас, если у вас не установлена Visual Studio Premium или выше, но важно понять, что стоит за этим, если вы собираетесь использовать контракты кода.
Когда вы применяете контракт к функции, это буквально контракт . Эта функция гарантирует, что она будет вести себя в соответствии с договором, и гарантированно будет использоваться только так, как определено договором.
В данном примере
DoSomethingElse()
функция не соответствует договору, как указаноDoSomething()
, так как можно передать значение null, и статическая проверка укажет на эту проблему. Чтобы решить эту проблему, добавьте тот же контракт кDoSomethingElse()
.Теперь это означает, что будет дублирование, но это дублирование необходимо, так как вы решаете предоставить функциональность двум функциям. Эти функции, хотя и частные, также могут вызываться из разных мест в вашем классе, поэтому единственный способ гарантировать, что из любого данного вызова аргумент никогда не будет нулевым, - это дублировать контракты.
Это должно заставить вас пересмотреть, почему вы в первую очередь разделяете поведение на две функции. Мое мнение ( вопреки распространенному мнению ) всегда заключалось в том, что нельзя разделять функции, которые вызываются только из одного места . Обнаружение инкапсуляции путем применения контрактов становится еще более очевидным. Кажется, я нашел дополнительную аргументацию для своего дела! Спасибо! :)
источник