2008-09-14 3 views
33

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

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

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

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

Итак, я думаю, что я получаю 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, так и в релизах) ... или нет. Что мне не хватает?

ответ

48

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

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

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

+10

Утверждение может быть использовано для проверки параметра * Метод внутренних * звонков (так называемого кодом, принадлежащего к той же компоненте) методам, в отличие от внешних вызовов методов (так называемых другим компонент) , Например, я могу утверждать, что параметр частного метода типа Double не является NaN. – RoadWarrior 2008-10-31 11:48:52

2

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

4

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

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

17

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

Предположим, у нас есть класс пользователя с свойством Name и методом ToString.

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

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

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

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

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

    return Name; 
} 

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

Реализация как с

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

    return Name; 
} 

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

3

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

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

Также, если исключение будет необработанным, еще лучше использовать мини-свалку. Одна область, где VB является более мощной, чем C#, заключается в том, что вы можете использовать фильтр исключений для привязки мини-дампа, когда исключение находится в полете, и оставить остальную обработку исключений без изменений. Gregg Miskelly's blog post on exception filter inject - полезный способ сделать это из C#.

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

0

Здесь на 2 цента.

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

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

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

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

4

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

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

Хорошие правила большого пальца я прибыл в:

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

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

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

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

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

1

Пример хорошего использования 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 

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

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

cf.http://c2.com/cgi/wiki?FailFast скопированы и изменен с Java вопроса: Exception Vs Assertion

Смежные вопросы