Подтвердить что ты не робот

Debug.Assert vs Исключения

Удивительно, но я только смог найти один предыдущий вопрос о SO по этому вопросу, и я просто хотел бы получить сообщество "Голосуй за доверие" (или нет!) по моему подходу.

Таким образом, я вижу:

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

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

Скажем, у нас есть класс 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), пользователь класса не получит полезную информацию, которую они оставили в недопустимом состоянии. Но это как-то противоречит другой мантре, которая говорит только об исключении, когда происходят вещи, совершенно вне вашего контроля.

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

Изменить: я заметил этот ответ в связанном SO-вопросе (Дизайн по контракту с использованием утверждений или исключений?):

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

Во-первых, допустимый 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 недействителен. Это отправит пользователю четкое сообщение и, таким образом, решит вашу проблему.

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

Изменить: О утверждениях и режиме выпуска см. также:

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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