Debug.Assert против.Конкретные выброшенные исключения

StackOverflow https://stackoverflow.com/questions/61219

  •  09-06-2019
  •  | 
  •  

Вопрос

Я только начал просматривать «Отладку приложений MS .Net 2.0» Джона Роббинса, и меня смутила его пропаганда Debug.Assert(...).

Он отмечает, что хорошо реализованные Asserts хранят состояние, в некоторой степени, состояния ошибки, например:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

Лично мне кажется безумием, что он так любит повторять свой тест без действительно разумного комментария «бизнес-логики», возможно, «i <= 3 никогда не должно случиться из-за процесса виджетификации flobittyjam».

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

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

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

Что я получил от повторения теста состояния ошибки Debug.Assert?Думаю, я бы понял, если бы мы говорили о двойной проверке очень важного вычисления только для отладки...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

... но я не понимаю этого для тестов параметров, которые, безусловно, стоит проверить (как в сборках DEBUG, так и в Release)...или нет.Что мне не хватает?

Это было полезно?

Решение

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

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

Надеюсь это поможет!

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

Существует коммуникационный аспект между утверждениями и выдачей исключений.

Допустим, у нас есть класс User со свойством Name и методом ToString.

Если ToString реализован следующим образом:

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

В нем говорится, что имя никогда не должно иметь значение null, и если это так, в классе User есть ошибка.

Если ToString реализован следующим образом:

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

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

Реализация с обоими

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

говорит, что если имя равно нулю, то в классе User есть ошибка, но мы все равно хотим ее обработать.(Пользователю не нужно проверять имя перед звонком.) Я думаю, что именно такую ​​безопасность рекомендовал Роббинс.

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

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

Хорошие эмпирические правила, к которым я пришел:

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

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

  3. Если утверждение срабатывает (либо в модульном тесте, либо во время тестирования), класс содержит ошибку.

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

По поводу дублирования:Я согласен.Я не понимаю, зачем вам повторять проверку с помощью Debug.Assert И проверки исключений.Это не только вносит некоторый шум в код и путает информацию о том, кто виноват, но и является формой повторения.

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

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

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

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

Кроме того, если исключение не будет обработано, еще лучше будет создать мини-дамп.Одна из областей, в которой VB более эффективен, чем C#, заключается в том, что вы можете использовать фильтр исключений для создания мини-дампа, когда исключение находится в процессе выполнения, и оставить остальную часть обработки исключений неизменной. Сообщение в блоге Грегга Мискелли о внедрении фильтра исключений предоставляет полезный способ сделать это из C#.

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

ИМХО, это всего лишь потеря времени на разработку.Правильно реализованное исключение дает вам четкое представление о том, что произошло.Я видел слишком приложения, показывающие неясное сообщение «Утверждение не выполнено:я < 10" ошибок.Я рассматриваю утверждение как временное решение.На мой взгляд, в финальной версии программы никаких утверждений быть не должно.В своей практике я использовал утверждения для быстрых и грязных проверок.Финальная версия кода должна учитывать ошибочную ситуацию и вести себя соответствующим образом.Если случится что-то плохое, у вас есть 2 варианта:справься с этим или оставь это.Функция должна генерировать исключение со значимым описанием, если переданы неправильные параметры.Я не вижу смысла в дублировании логики валидации.

Пример хорошего использования Assert:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

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

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

ср. http://c2.com/cgi/wiki?FailFastскопировано и изменено из вопроса Java: Исключение против утверждения

Вот на 2 цента.

Я думаю, что лучший способ — использовать как утверждения, так и исключения.Основные различия между этими двумя методами, по моему мнению, заключаются в том, что операторы Assert можно легко удалить из текста приложения (определяют, условные атрибуты...), тогда как создаваемое исключение зависит (как правило) от условного кода, который сложнее удалить ( multine раздел с условными выражениями препроцессора).

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

Если вы передаете нулевую ссылку на объект в качестве стандартного параметра и используете это значение, вы получаете исключение нулевого указателя.Действительно:зачем писать утверждение?В данном случае это пустая трата времени.Но как насчет частных членов класса, используемых в подпрограммах класса?Когда эти значения где-то установлены, лучше проверить с помощью утверждения, установлено ли нулевое значение.Это происходит только потому, что при использовании этого члена вы получаете исключение нулевого указателя, но не знаете, как было установлено это значение.Это приводит к прерыванию перезапуска программы при использовании всех точек входа для установки закрытого члена.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top