Когда я должен использовать Debug.Assert ()? - PullRequest
212 голосов
/ 24 сентября 2008

Я работаю профессиональным инженером-программистом около года, закончив со степенью бакалавра. Некоторое время я знал об утверждениях в C ++ и C, но понятия не имел, что они существовали в C # и .NET вообще до недавнего времени.

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

Должен ли я начать использовать Asserts в нашем производственном коде? И если да, то когда его использование является наиболее подходящим? Будет ли больше смысла делать

Debug.Assert(val != null);

или

if ( val == null )
    throw new exception();

Ответы [ 20 ]

7 голосов
/ 25 сентября 2008

Согласно стандарту IDesign , вы должны

Утверждение каждого предположения. В среднем каждая пятая строка является утверждением.

using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

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

6 голосов
/ 30 декабря 2013

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

Debug.Assert(true);

Потому что он проверяет то, что вы уже предположили, правда. E.g.:

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

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

В первом случае проблем нет.

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

В третьем случае есть проблема с этим кодом, потому что он должен был уже проверить, что en != null еще до того, как он был вызван, так что это не правда, это ошибка. Или, другими словами, это должен быть код, который теоретически можно оптимизировать до Debug.Assert(true), sicne en != null всегда должен быть true!

6 голосов
/ 24 сентября 2008

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

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

4 голосов
/ 18 ноября 2015

Цитата взята из Прагматичный программист: от подмастерья до мастера

Оставить утверждения включенными

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

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

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

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

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

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

4 голосов
/ 21 декабря 2013

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

1) Что-то, что я не видел, упомянуто здесь, это дополнительное концептуальное покрытие, которое Asserts может обеспечить во время автоматического тестирования . В качестве простого примера:

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

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

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

2) Кроме того, некоторые тесты просты в написании, но дорогостоящи и не нужны, если исходные предположения . Например:

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

3) Далее, в некоторых случаях ваш продукт может не иметь полезного диагностического взаимодействия для всех или части его операций при развертывании в режиме выпуска . Например:

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

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

Если основная операция String.Find сообщает, что она вернет -1, когда критерии поиска не найдены, вы можете безопасно выполнить одну операцию вместо трех. Однако, если оно действительно вернуло -2, у вас может не быть разумных действий. Было бы бесполезно заменять более простой расчет на тот, который проверяет отдельно значение -1, и в большинстве сред релизов нецелесообразно засорять ваш код тестами, обеспечивающими работу основных библиотек, как ожидается. В этом случае утверждения идеальны.

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

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

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

0 голосов
/ 31 июля 2015

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

В первом случае утверждения даже не обязательно должны быть в конечном коде. Вы должны использовать Debug.Assert во время разработки, и вы можете удалить их, если / когда больше не нужны. Если вы хотите оставить их или забыть удалить их, не проблема, так как они не будут иметь никакого значения в компиляциях Release.

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

Trace.Assert имеет идеальный способ для достижения этой цели. Он не будет удален в работе и может быть настроен для разных слушателей с помощью app.config. Таким образом, для разработки подходит стандартный обработчик, а для производства вы можете создать простой TraceListener, как показано ниже, который генерирует исключение и активирует его в файле конфигурации производства.

using System.Diagnostics;

public class ExceptionTraceListener : DefaultTraceListener
{
    [DebuggerStepThrough]
    public override void Fail(string message, string detailMessage)
    {
        throw new AssertException(message);
    }
}

public class AssertException : Exception
{
    public AssertException(string message) : base(message) { }
}

А в файле производственного конфигурации:

<system.diagnostics>
  <trace>
    <listeners>
      <remove name="Default"/>
      <add name="ExceptionListener" type="Namespace.ExceptionTraceListener,AssemblyName"/>
    </listeners>
  </trace>
 </system.diagnostics>
0 голосов
/ 24 сентября 2008

Вы должны использовать Debug.Assert для проверки на логические ошибки в ваших программах. Компилятор может информировать вас только о синтаксических ошибках. Таким образом, вы должны определенно использовать операторы Assert для проверки на логические ошибки. Как, например, тестирование программы, которая продает автомобили, которые только синие BMW должны получить скидку 15%. Компилятор может ничего вам не сказать, если ваша программа логически верна в выполнении этого, но оператор assert может.

0 голосов
/ 24 сентября 2008

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

Также нужно быть осторожным в asp.net, так как assert может отображаться на консоли и блокировать запрос (ы).

0 голосов
/ 24 сентября 2008

Я не знаю, как это в C # и .NET, но в C assert () будет работать, только если скомпилировано с -DDEBUG - конечный пользователь никогда не увидит assert (), если скомпилировано без. Это только для разработчиков. Я использую его очень часто, иногда легче отслеживать ошибки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...