2009-02-03 5 views
16

Удивительно, но я только смог найти один предыдущий вопрос о SO по этому вопросу, и я бы просто хотел получить сообщество «Голосую за доверие» (или нет!) По моему подходу.Debug.Assert vs Exceptions

Как я вижу это таким образом:

  • использование Debug.Assert для государственных вещей, которые вы могли бы ожидать быть правдой. Это будет использоваться, когда мы полностью контролируем нашу среду, например, в методе проверяем некоторые предварительные и последующие условия.
  • Использование Исключения при возникновении особых обстоятельств. Работа с внешними ресурсами, то есть файлами, базами данных, сетями и т. Д., Не требует больших усилий. Но ...

Это выглядит немного мутным в следующем сценарии. Обратите внимание, что это КОНСТРУКТИВНЫЙ ПРИМЕР только для иллюстрации!

Скажем, у нас есть класс MyClass, который имеет общедоступное свойство MyMode и метод GetSomeValueForCurrentMode(). Подумайте о MyClass как о том, что нужно отгрузить (релиз построен) в библиотеке для использования другими разработчиками.

Мы ожидаем, что MyMode будет обновлен внешними пользователями этого класса. Теперь GetSomeValueForCurrentMode() имеет следующую логику:

switch(MyMode) 
{ 
case Mode.ModeA: 
return val1; 
case Mode.ModeB: 
return val2; 
default: 
//Uh-uh this should never happen 

} 

Что я клоню здесь является то, что пользователь MyClass оставил его в нерабочем состоянии. так что нам делать?

По умолчанию, должны ли мы Debug.Assert или throw new InvalidOperationException (или другие)?

Существует одна мантра, в которой говорится, что мы не должны доверять пользователям наших классов. Если мы выберем Debug.Assert и построим MyClass в качестве сборки релиза (таким образом удалив аргументы Debug), пользователь класса не получит полезную информацию, которую они оставили в недопустимом состоянии. Но это как-то противоречит другой мантре, в которой говорится только об исключении, когда происходят вещи, совершенно не зависящие от вашего контроля.

Я нахожусь в кругах с этим - один из тех дискуссий по программированию, которые, похоже, не имеют окончательного «правильного» ответа. Так что давайте поставим это на голосование!

Edit: я заметил этот ответ в родственной SO вопроса (Design by contract using assertions or exceptions?):

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

Для меня это имеет смысл и может сочетаться с техникой «Assert then throw», описанной ниже.

Мысли приветствуются!

ответ

5

Я согласен с большинством людей здесь и следую проектам по контракту. Вы должны четко и четко различать требования в развернутом коде (Контракты) и вычислять ожидаемое состояние во время проектирования (Отладка утверждений).

Вы должны ВСЕГДА бросать утверждения контракта в качестве исключений (поскольку они всегда должны быть исключительными). Есть механизмы, встроенные в большинство фреймворков для обнаружения утверждений отладки. Но во время выполнения вы всегда должны исключать исключение.

Я использую пользовательскую библиотеку, чтобы помочь с этим (в C#/VB.NET). Недавно я поставил его на Codeplex (http://www.contractdriven.com/), если вам интересно, как это работает на практике.

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

Итак, вопрос в вашем оригинальном посте ... «Что я получаю отсюда, так это то, что пользователь MyClass оставил его в недопустимом состоянии. Так что нам делать?» ... никогда не должно возникать.

Возможно, вам больше не понадобится отлаживать что-либо еще! ;-)

+2

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

+1

Y. Извините всех. Я удалил ссылку на «Fail Fast». Если вы хотите FailFast, вы можете использовать Environment.FailFast() в C#, а не исключать исключение. Но просто знайте, что ваши блоки finally не будут работать. – ChrisV

+0

Рассмотрите возможность использования [fluentassertions] (http://fluentassertions.com/). Это помогает создавать читаемые контракты и в то же время выдает исключения, если контракты нарушаются. –

0

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

0

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

2

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

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

+0

Stick with Asserts, даже если MyClass упакован и выпущен как библиотека для других разработчиков? – Duncan

+0

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

5

Во-первых, действительный MyClass должен, конечно, выражаться в инварианте MyClass .

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

void Setter(mode m) 
    { 
    // INVARIANT ASSERT (1) 
    // PRECONDITION ASSERTS (uses "m") (2) 

    // BODY (3) 

    // POSTCONDITION ASSERTS (if any) (4) 
    // INVARIANT ASSERT (5) 
    } 

В (5) вам не удастся с кричащим утверждением, что инвариант не выполняется. Но в (2) вы потерпите неудачу раньше, потому что недействительный код m. Это отправит пользователю четкое сообщение и, таким образом, решит вашу проблему.

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

Edit: О утверждений и режиме выпуска, смотрите также:

4

Часто оба: Assert, затем throw.

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

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

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

+0

Спасибо, это имеет большой смысл и похоже на мое мышление. Я оставлю это в сообществе SO, чтобы решить, согласны ли они! – Duncan

+1

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

+1

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

5

Я в принципе согласен с заключением своего вопроса: если код вшей обнаруживает ошибку A вши сделала, это случай для ssert (и утверждение должно быть оставлен на в рабочем коде, если производительность диктует иначе). Если код Алисы обнаружил ошибку в E код ve, это случай для E xceptions, предполагая, что Алиса и Ева находятся на противоположных сторон вашего программного обеспечения для отслеживания ошибок.

Теперь это общее правило. Утверждения, в слегка измененной форме, также могут использоваться как «хедз-ап, разработчик!». (а затем их не следует называть «ASSERT», но «HEADS_UP» или что-то подобное). Что делать, если ваша компания разрабатывает клиент/серверный продукт, а сервер отправляет недействительные данные клиенту? Если вы клиентский программист, вам кажется, что рассматривайте его как внешние данные (это данные Eve, которые являются kaputt), и вы хотите исключить исключение. Но «более мягкое» утверждение, которое отлаживает отладчик Visual Studio прямо там, может быть очень хорошим способом обнаружить эти проблемы на самом раннем этапе и сообщить об этом команде сервера. В реальной установке вполне может быть закат Мэллори с данными между Евой и Алисой, но большую часть времени это действительно ошибка одного из ваших коллег, и вы хотите увидеть это, когда это произойдет - вот почему я их называю «хедз-ап» утверждают: они не заменяют исключения, но они дают вам предупреждение и возможность проверить проблему.

1

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

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