как мне объяснить, что проверки if (xyz == null) не являются защитными

у меня есть несколько разработчиков, которые постоянно ставят проверки If null

Например:

Run(Order order)
{
  if (order == null) return;
}

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

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

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

какие-нибудь хорошие статьи или предложения, которые помогут мне донести эту мысль?


person leora    schedule 30.10.2009    source источник
comment
Вы можете перефразировать свой вопрос; это слишком субъективно в его нынешнем виде. К тому же ты ошибаешься :-)   -  person ChssPly76    schedule 30.10.2009
comment
я перефразировал. . почему ты говоришь, что я неправ ??   -  person leora    schedule 30.10.2009
comment
Хм, проголосовали за открытие после того, как правки снизили тон.   -  person Dana    schedule 30.10.2009
comment
Если ваш контракт конструктора требует аргумента, отличного от NULL, вместо этого вы должны: if (order == null) throw new ArgumentNullException (order);   -  person RMorrisey    schedule 30.10.2009
comment
@oo - потому что в исходной форме (лучше отредактированной) ваше сообщение подразумевает, что всегда проверять на пустые значения - плохая идея. И это неправильно, потому что это зависит от варианта использования - я полностью согласен с ответами John Ferminella и JS Bangs ниже.   -  person ChssPly76    schedule 30.10.2009
comment
Разве вы не объяснили, почему? :)   -  person Ed S.    schedule 30.10.2009
comment
Вы должны полностью выбросить исключение, потому что любой следующий код может предполагать, что метод Run (order) не завершился ошибкой. Отверстия в цепочке. Даже если при построении всего приложения сейчас оно работает нормально, это станет будущей ошибкой, когда кто-то попытается вызвать тот же метод Run из другого контекста, и он потерпит неудачу, а они не знают. У вас будет что-то еще, что выйдет из строя, и удачи в выяснении того, что это было, когда это даже больше не отображается в стеке вызовов.   -  person John K    schedule 30.10.2009
comment
@jdk: Это должен быть ответ, а не комментарий.   -  person SLaks    schedule 30.10.2009


Ответы (9)


Если ваш класс не может принимать null аргумент, лучше всего сделать следующее:

if (arg == null)
    throw new ArgumentNullException();

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

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

person JSBձոգչ    schedule 30.10.2009
comment
если в контракте указано, что null - это нормально, вам, вероятно, следует использовать значение null, чтобы более явно указать это. - person RCIX; 30.10.2009

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

person John Feminella    schedule 30.10.2009

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

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

Хочу добавить, что ArgumentNullException не зря принимает параметр.

Лучше написать

if(myArg == null) throw new ArgumentNullException("myArg");

чем бросать ArgumentNullException без paramName.

Таким образом, если у вас есть исключение из функции, которая принимает пять параметров, вы будете знать, какой из параметров вызвал проблему. Это особенно важно, если вы не можете подключить отладчик. (Например, на производственном веб-сервере или компьютере конечного пользователя)

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

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Check for null arguments</Title>
            <Shortcut>tna</Shortcut>
            <Description>Code snippet for throw new ArgumentNullException</Description>
            <Author>SLaks</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>Parameter</ID>
                    <ToolTip>Paremeter to check for null</ToolTip>
                    <Default>value</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$");
        $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>
person SLaks    schedule 30.10.2009

Мы надеемся, что кодовые контракты в .net 4.0 сделают это поведение более последовательным. Любые статьи, в которых говорится о контрактах кода, помогут донести идею, и в будущем такой синтаксис предоставит метод.

http://blogs.msdn.com/bclteam/archive/2008/11/11/introduction-to-code-contracts-melitta-andersen.aspx

person jsimmons    schedule 30.10.2009
comment
К сожалению, похоже, что Code Contracts не будет доступен в профессиональной версии Visual Studio 2010. Это предотвратит рост if. - person Wim Coenen; 30.10.2009
comment
Фактические контракты - это функция CLR, которая будет общей для всех версий (включая Mono, в конечном итоге). То, что ограничено командной студией, похоже, является приложением, которое исследует код и проверяет соответствие контракту. Проверка времени выполнения будет доступна везде. - person jsimmons; 30.10.2009
comment
Классы Contract действительно находятся в BCL (System.Diagnostics.Contracts), но на самом деле они ничего не делают без инструментов; Я пробовал это в VS2010 Premium beta2: stackoverflow.com/questions/1608406/ - person Wim Coenen; 31.10.2009

Я не смотрел его, но на eiffel.com есть 2 презентации (слайды + аудио ) по теме дизайн по контракту. Эти ребята изобрели концепцию, так что если кто-то может объяснить это, так это они :-)

person Wim Coenen    schedule 30.10.2009

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

person Spike Williams    schedule 30.10.2009
comment
Вы предполагаете, что они пишут модульные тесты. Разработчики, которые пишут модульные тесты, обычно уже выбрасывают исключения. (Я надеюсь) - person SLaks; 30.10.2009

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

Debug.Assert( item != null, "Null items are not supported );

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

person Phillip Ngan    schedule 30.10.2009
comment
Если вас так беспокоит производительность, что вы не хотите сравнивать ссылку на null, вам следует писать ассемблер. Кроме того, вы говорите, что в продакшене ошибок не бывает? - person SLaks; 30.10.2009
comment
Не говоря уже о неявных нулевых проверках при каждом доступе к члену, которые есть в любом случае ... - person Pavel Minaev; 30.10.2009

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

person Community    schedule 30.10.2009

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

Все ошибки оказываются или кажутся глупыми, когда они обнаруживаются. Но вот такие проверки заставляют их разоблачать себя. А пока ошибок не видно. А для достаточно больших и сложных проектов вы не знаете, кто найдет ошибку и при каких условиях она будет обнаружена. Код, разработанный для обеспечения устойчивости, обычно имеет такие проверки повсюду, а также проверяет возвращаемые значения для каждой функции, которая должна включать значения ошибок. Таким образом, вы в конечном итоге кодируете семантику «Я не могу этого сделать, потому что подфункции, на которые я полагаюсь, не работают», которая на самом деле обрабатывается правильно. Большая ценность этого заключается в том, что вы можете довольно легко реализовать обходные пути или инструменты отладки с самооценкой. Почему вы хотите делать такие вещи, потому что самые сложные ошибки обычно зависят от обоих этих свойств для правильной отладки.

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

Вкратце: ни один код не может стать надежным, если предположить надежность окружающей среды.

person Paul Hsieh    schedule 30.10.2009
comment
Ты неправ. Он считает, что отказоустойчивый код значительно упрощает поиск ошибок в вызывающем его коде, и он очень прав. - person SLaks; 30.10.2009
comment
Ждать; какую сторону ты защищаешь? - person SLaks; 30.10.2009
comment
Быстрый отказ полезен только для разработчика с исходным кодом и отладчиком прямо перед ним / ней, и если они делают действительно действительно простую ошибку. С этим также столкнутся тестировщики, бета-программисты и разработчики, работающие над другими частями кода. И если они быстро потерпят неудачу, что они собираются с этим делать? Если вы получаете неверные параметры, то вы хотите знать, ПОЧЕМУ вы получаете неверные параметры. САЙТ ВЫЗОВА будет знать об этом больше, чем функция, которая пытается использовать неверный параметр. - person Paul Hsieh; 30.10.2009
comment
К сожалению, если вы посмотрите на пример, представленный на плакате, он почти наверняка просто поглотит ошибку в черную дыру, из которой никакая информация не ускользнет. Это позволяет ошибке длиться намного дольше. С другой стороны, если код защиты по крайней мере регистрирует сообщение о том, что произошло (нулевой аргумент в XXX, все ставки отключены! Исследуйте !!!) в журнале, который кто-то действительно проверит, тогда я могу согласиться с вами. - person Michael H.; 30.10.2009
comment
Имейте в виду, что оригинальный плакат заранее заявил о своих предубеждениях по этому поводу. Так что тот факт, что он показал бесполезный пример кода, не следует воспринимать как свидетельство чего-либо. Конечно, у вас должны быть очень наглядные ошибки. Возвращение перечисления, например: APP_ERR_NULL_FOO_OBJECT, которое затем замечает сайт вызова, потенциально очищает и регистрирует как errLogf (Ошибка:% s foo (% d), errString (APP_ERR_NULL_FOO_OBJECT), parameterToInitFoo); или что-то в этом роде, то это идеально. Вы не можете понять или оценить ценность этого с позиции / точки зрения спрашивающего. - person Paul Hsieh; 30.10.2009
comment
@Dan: Это очень плохой дизайн. Вы не хотите дублировать каждую проверку параметров на сайтах вызовов. Это будет полагаться на все сайты обзвона. Нулевое значение - не единственный способ, по которому такой параметр или сама функция могут пойти не так. Я не имел в виду буквально вызвать errString (APP_ERR ...). Очевидно, это будет что-то вроде errString (ret), которое будет соответствовать той ошибке, которая была возвращена вызываемой функцией. - person Paul Hsieh; 30.10.2009