Debug.Assert против определенных исключений - PullRequest
33 голосов
/ 14 сентября 2008

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

Он указывает, что правильно реализованные Утверждения хранят состояние, в некоторой степени, состояния ошибки, например ::100100

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

Теперь лично мне кажется безумным, что он так любит перезапускать свой тест без реального разумного комментария «бизнес-логики», возможно, «я <= 3 никогда не должен происходить из-за процесса виджирования flobittyjam». </p>

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

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

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) ... или нет. Чего мне не хватает?

Ответы [ 8 ]

48 голосов
/ 14 сентября 2008

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

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

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

17 голосов
/ 14 сентября 2008

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

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

Если ToString реализован так:

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

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

Если ToString реализован так:

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

     return Name;
}

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

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

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

     return Name;
}

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

5 голосов
/ 09 января 2010

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

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

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

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

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

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

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

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

4 голосов
/ 14 сентября 2008

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

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

3 голосов
/ 15 сентября 2008

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

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

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

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

2 голосов
/ 14 сентября 2008

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

1 голос
/ 11 мая 2011

Пример правильного использования 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: Исключение против утверждения

0 голосов
/ 09 января 2010

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

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

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

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

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

...