Коли я повинен використовувати Debug.Assert ()?


220

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

Наш виробничий код не містить жодних тверджень і моє питання в цьому ...

Чи варто починати використовувати Ассерти у нашому виробничому коді? І якщо так, то коли його використання є найбільш доцільним? Чи було б більше сенсу робити

Debug.Assert(val != null);

або

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

2
Дихотомія, яку ви встановили, є підказкою. Це не питання про - або про винятки та твердження, і про оборонний код. Коли робити, що саме ви шукаєте, щоб зрозуміти.
Каспер Леон Нільсен

5
Колись я читаю, хтось припускає, що виняток чи інший метод збоїв є відповідним умовам, коли "я не можу розумно відновитись після цього", а також додаткові твердження підходять для умов, коли "Це ніколи не повинно відбуватися ніколи". Але які реалістичні обставини відповідають останнім умовам, не задовольняючи також перших? Виходячи з фону Python, де твердження залишається у виробництві, я ніколи не розумів підходу Java / C # до вимкнення частини вашої перевірки на виробництві. Єдиний випадок, який я дійсно можу побачити, - це якщо перевірка дорога.
Марк Амерді


2
Особисто я використовую винятки для публічних методів, а твердження - для приватних методів.
Фред

Відповіді:


230

У налагодженні програм Microsoft .NET 2.0 Джон Роббінс має великий розділ щодо тверджень. Його основні моменти:

  1. Стверджуйте вільно. Ніколи не можна мати занадто багато тверджень.
  2. Твердження не замінюють винятків. Винятки стосуються речей, які вимагає ваш код; твердження охоплюють речі, які він передбачає.
  3. Добре написане твердження може розповісти вам не просто про те, що сталося і де (як виняток), а й чому.
  4. Повідомлення про виняток часто може бути криптовалютним, вимагаючи від вас працювати за допомогою коду, щоб відтворити контекст, який спричинив помилку. Затвердження може зберегти стан програми в момент виникнення помилки.
  5. Твердження подвоюються як документація, розповідаючи іншим розробникам, від яких припущень залежить ваш код.
  6. Діалогове вікно, що з’являється, коли твердження не вдається, дозволяє вам приєднати налагоджувач до процесу, так що ви можете просуватися навколо стека, як ніби ви поставили туди точку розриву.

PS: Якщо вам сподобався Code Complete, рекомендую дотримуватися його з цією книгою. Я купив його, щоб дізнатися про використання WinDBG та дамп-файлів, але перша половина рясніє порадами, які допоможуть уникнути помилок в першу чергу.


3
+1 за стисле та корисне резюме. Дуже безпосередньо застосовна. Головне, чого мені не вистачає, це коли використовувати Trace.Assert vs. Trace.Assert. Тобто щось про те, коли ви робите / не хочете їх у своєму виробничому коді.
Джон Кумбс

2
JonCoombs - "Trace.Assert vs. Trace.Assert", помилка друку?
thelem

1
@thelem Можливо, Джон мав на увазі Debug.Assertпроти Trace.Assert. Останнє виконується як у складанні Release, так і у збірці налагодження.
DavidRR

Чому я віддаю перевагу Debug.Assert перед викиданням винятку?
Barış Akkurt

86

Вказуйте Debug.Assert()всюди в коді, де ви хочете, щоб перевірити здоровість, щоб забезпечити інваріантів. Коли ви компілюєте збірку випуску (тобто немає DEBUGконстанти компілятора), виклики до Debug.Assert()буде видалено, щоб вони не впливали на продуктивність.

Ви все одно повинні кидати винятки, перш ніж дзвонити Debug.Assert(). Затвердження просто гарантує, що все так, як очікувалося, поки ви все ще розвиваєтеся.


35
Чи можете ви пояснити, чому ставите твердження, якщо ви все-таки кидаєте виняток, перш ніж називати його? Або я неправильно зрозумів вашу відповідь?
Роман Старков

2
@romkyns Ви все одно повинні включати їх, оскільки якщо ви цього не зробите, коли ви будуєте проект у режимі випуску , всі перевірки / перевірку помилок не будуть.
Оскар Медерос

28
@Oscar Я подумав, що в цьому полягає вся суть використання тверджень ... Добре, тоді ви ставите винятки перед ними - тоді навіщо ставити твердження після?
Роман Старков

4
@superjos: Я не погоджуюся: у пункті №2 у відповіді MacLeod зазначено, що вам дійсно потрібно твердження І винятки, але не в тому самому місці. Закидати NullRefEx на змінну і відразу після неї запустити марно (метод assert ніколи не покаже вікно діалогу в цьому випадку, що є сутью ствердження). Що означає MacLeod, це те, що в деяких місцях вам знадобиться виняток, в інших вистачить Assert.
Девід

1
Мені може стати безладним тлумачення моєї інтерпретації чужої відповіді :) У всякому разі, я з вами з цього приводу: вам потрібно обоє, і ви не повинні ставити виняток перед ствердженням. Я не впевнений у значенні "не в тому самому місці". Знову ж, відмовившись від тлумачення, я просто викладу свої думки / уподобання: поставлю одне або кілька тверджень, щоб перевірити передумови до початку якоїсь операції, або перевірити постумови після операції. Окрім тверджень, а також після них, будь-ласка, перевірте, чи щось піде не так і чи потрібно викидати винятки.
superjos

52

Від коду завершено

8 Оборонне програмування

8.2 Твердження

Твердження - це код, який використовується під час розробки - зазвичай це рутина або макрос - який дозволяє програмі перевірити себе під час роботи. Коли твердження вірно, це означає, що все працює так, як очікувалося. Якщо це неправда, це означає, що він виявив несподівану помилку в коді. Наприклад, якщо система передбачає, що файл інформації про клієнтів ніколи не матиме більше 50 000 записів, програма може містити твердження, що кількість записів менша або дорівнює 50 000. Поки кількість записів менше або дорівнює 50 000, твердження буде мовчати. Однак якщо у нього зустрічається більше 50 000 записів, він голосно "стверджує", що в програмі є помилка.

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

Затвердження зазвичай бере два аргументи: булевий вираз, який описує припущення, яке повинно бути правдивим, і повідомлення, яке відображається, якщо його немає.

(…)

Зазвичай ви не хочете, щоб користувачі бачили повідомлення про твердження у виробничому коді; твердження в першу чергу для використання під час розробки та обслуговування. Твердження зазвичай складаються в код на час розробки та складаються з коду для виробництва. Під час розвитку твердження вимикають суперечливі припущення, несподівані умови, погані значення, що передаються підпрограми тощо. Під час виробництва вони складаються з коду, щоб твердження не погіршували продуктивність системи.


7
Отже, що станеться, якщо файл інформації про клієнтів, який зустрічається у виробництві, містить понад 50 000 записів? Якщо твердження складено з виробничого коду, і ця ситуація не вирішується інакше, чи це не заклинання?
DavidRR

1
@DavidRR Так. Але як тільки виробництво подає сигнал про проблему, і якийсь розробник (який може не знати цей код добре) налагоджує проблему, твердження буде невдалим, і розробник негайно дізнається, що система використовується не за призначенням.
Марк

48

FWIW ... Я вважаю, що мої загальнодоступні методи, як правило, використовують if () { throw; }шаблон, щоб гарантувати правильність виклику методу. Мої приватні методи, як правило, використовують Debug.Assert().

Ідея полягає в тому, що з моїми приватними методами я перебуваю під контролем, тож якщо я почну викликати один із своїх власних приватних методів з параметрами, які є невірними, то я десь порушив своє власне припущення - я ніколи не повинен був отримати в цей стан. На виробництві ці приватні твердження в ідеалі повинні бути зайвою роботою, оскільки я повинен підтримувати свій внутрішній стан дійсним і послідовним. На відміну від параметрів, наданих для публічних методів, які можуть викликати будь-хто під час виконання: мені все ж потрібно застосувати обмеження параметрів, кидаючи винятки.

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


3
Це дуже чіткий опис належної практики і дає дуже розумну відповідь на поставлене питання.
Каспер Леон Нільсен

42

Використовуйте твердження, щоб перевірити припущення розробника та винятки, щоб перевірити припущення щодо навколишнього середовища.


31

Якби я був ти, я би робив:

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

Або уникати повторної перевірки стану

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}

5
Як це вирішує проблему? З цим debug.assert стає безглуздим.
Дивовижний

43
Ні, це не так - він розбивається на код безпосередньо перед викидом виключення. Якщо у вас є код спробувати / ловити десь ще в коді, ви можете навіть не помітити винятку!
Позначити Інграма

2
+1 У мене було чимало проблем, коли люди просто намагалися / ловити винятки, не роблячи нічого, тому відстеження помилок було проблемою
dance2die

5
Я припускаю, що є випадки, коли ви можете це зробити, але ніколи не слід спіймати загальний виняток!
Casebash

8
@MarkIngram -1 до вашої відповіді та +1 до вашого коментаря, що виправдовує це. Це приємний трюк для певних особливих обставин, але здається, що це взагалі химерна справа для всієї перевірки.
Марк Амері

24

Якщо ви хочете активувати у виробничому коді (тобто, випускати версії), ви можете використовувати Trace.Assert замість Debug.Assert.

Це, звичайно, додає накладні витрати на ваше виробництво.

Крім того, якщо ваша програма працює в режимі користувальницького інтерфейсу, діалогове вікно «Затвердження» відображатиметься за замовчуванням, що може трохи заважати вашим користувачам.

Ви можете змінити цю поведінку, видаливши DefaultTraceListener: подивіться документацію для Trace.Listeners в MSDN.

Підсумовуючи це,

  • Використовуйте Debug.Asersert вільно, щоб допомогти ловити помилки в налагодженнях налагодження.

  • Якщо ви використовуєте Trace.Assert в режимі користувальницького інтерфейсу, ви, ймовірно, хочете видалити DefaultTraceListener, щоб уникнути непорозуміння користувачів.

  • Якщо умова, яку ви тестуєте, - це те, що ваша програма не може впоратися, вам, ймовірно, краще кинути виняток, щоб забезпечити виконання не тривати. Майте на увазі, що користувач може вирішити ігнорувати твердження.


1
+1, щоб вказати на вирішальну відмінність Debug.Assert від Trace.Assert, оскільки ОП спеціально запитала про виробничий код.
Джон Кумбс

21

Застосування використовуються для лову помилки програміста, а не помилки користувача. Їх слід використовувати лише тоді, коли немає жодного шансу, що користувач може спричинити загострення ствердження. Наприклад, якщо ви пишете API, твердження не повинні використовуватися для перевірки того, що аргумент є недійсним у будь-якому методі, який користувач API може викликати. Але він може бути використаний у приватному методі, не відкритому як частина вашого API, щоб стверджувати, що ВАШ код ніколи не передає нульовий аргумент, коли цього не слід.

Зазвичай я віддаю перевагу виняткам над твердженнями, коли не впевнений.


11

Коротко

Asserts використовуються для охорони та для перевірки обмежень Design by Contract, а саме:

  • Assertsмає бути лише для налагодження та невиробничих версій. Асистенти, як правило, ігноруються компілятором у версіях версій.
  • Asserts може перевірити наявність помилок / несподіваних умов, які контролюються вашою системою
  • Asserts НЕ є механізмом перевірки первинного рядку вводу даних або правил бізнесу
  • Assertsслід НЕ використовуватися для виявлення несподіваних екологічних умов (які знаходяться поза контролем коду) , наприклад , з пам'яті, мережевого збою, збою бази даних і т.д. Хоча рідко, ці умови слід очікувати (і ваш код програми не може вирішити такі питання , як збій обладнання або виснаження ресурсів). Як правило, будуть викинуті винятки - тоді ваша програма може або вжити коригуючих дій (наприклад, повторити спробу бази даних або мережеву операцію, спробувати звільнити кешовану пам'ять), або абонентно перервати, якщо виняток не вдається обробити.
  • Невдале твердження повинно бути фатальним для вашої системи - тобто, на відміну від винятку, не намагайтеся зловити або не впоратися з помилкою Asserts- ваш код працює на несподіваній території. Сліди стеку та сміттєві відвали можна використовувати для визначення того, що пішло не так.

Твердження мають величезну користь:

  • Щоб допомогти у відсутності перевірки введених даних користувачів або помилок вище за поточним кодом вищого рівня.
  • Твердження в кодовій базі чітко передають читачеві припущення, зроблені в коді
  • Assert буде перевірено під час виконання в Debug збірках.
  • Після того, як код був вичерпно перевірений, відновлення коду, оскільки Release, видалить накладні витрати на перевірку припущення (але з вигодою, що пізніша збірка налагодження завжди скасує чеки, якщо потрібно).

... Детальніше

Debug.Assertвиражає умову, яка була прийнята про стан рештою блоку коду в межах управління програмою. Це може включати стан наданих параметрів, стан членів екземпляра класу або те, що повернення з виклику методу знаходиться в його контрактному / призначеному діапазоні. Як правило, твердження повинні переривати потік / процес / програму з усією необхідною інформацією (Trace Trace, Crash Dump тощо), оскільки вони вказують на наявність помилки або нерозглянутого стану, для якого не було розроблено (тобто не намагайтеся зловити або обробляти відмови у твердженні), за одним винятком випадків, коли саме твердження може завдати більше шкоди, ніж помилка (наприклад, контролери повітряного руху не хочуть YSOD, коли літак перебуває на підводному човні, хоча суперечить, чи слід розгортати збірку налагодження на виробництво ...)

Коли слід використовувати Asserts? - У будь-якій точці системи або API бібліотеки чи служби, де входи до функції або стану класу вважаються дійсними (наприклад, коли перевірка вже була зроблена на введенні користувача в ярусі презентації системи , класи класів бізнесу та рівня даних зазвичай передбачають, що нульові перевірки, перевірки діапазону, перевірки довжини рядків тощо при введенні вже зроблені). - Загальні Assertперевірки включають те, де неправильне припущення призведе до затримки нульового об'єкта, нульового дільника, переліку арифметичних чисел чи дат та загального поза діапазону / не призначеного для поведінки (наприклад, якщо для моделювання віку людини було використано 32-бітний інт , було б доцільно, щоб Assertвік насправді був від 0 до 125 або близько того - значення -100 і 10 ^ 10 не розраховані на).

.Net Code Contracts
У .Net Stack кодові контракти можна використовувати як додаток до, так і як альтернативу використаннюDebug.Assert . Код-контракти можуть додатково формалізувати перевірку стану та можуть допомогти у виявленні порушень припущень у ~ час збирання (або незабаром після цього, якщо вони працюватимуть як фонова перевірка в IDE).

Доступні чеки проектування за контрактом (DBC) включають:

  • Contract.Requires - Умовні умови
  • Contract.Ensures - Договір PostConditions
  • Invariant - висловлює припущення про стан об’єкта у всіх точках його життєдіяльності.
  • Contract.Assumes - заспокоює статичну перевірку, коли здійснюється виклик методів, не оформлених контрактом.

На жаль, договори кодексу майже не вмирають, оскільки MS припинила його розробляти.
Майк Лауні

10

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

Мені не подобається той факт, що він робить налагоджувальну функцію функціонально відмінною від версії версії. Якщо затвердження налагодження не вдається, але функціональність працює у випуску, то як це має сенс? Ще краще, коли стверджувач давно покинув компанію і ніхто не знає тієї частини коду. Тоді вам доведеться вбити частину свого часу, вивчаючи проблему, щоб побачити, чи справді це проблема чи ні. Якщо це проблема, то чому людина не кидає в першу чергу?

Для мене це пропонує, використовуючи Debug.Asserts, ви переносите проблему на когось іншого, вирішуйте проблему самостійно. Якщо щось має бути так, і це не так, киньте.

Я думаю, що можливо існують критичні сценарії щодо ефективності, коли ви хочете оптимізувати свої твердження, і вони там корисні, однак я ще не стикаюся з таким сценарієм.


4
Ви відповідаєте, заслуговує певної заслуги, хоча підкреслюючи деякі часто викликають занепокоєння щодо них, те, що вони переривають сесію налагодження і шанс на помилковий позитив. Однак вам не вистачає деяких тонкощів і ви пишете "оптимізувати заяви" - це може бути засноване лише на думці, що кидати виняток і робити налагодження. Це не так, вони служать різному призначенню та характеристикам, як ви бачите в деяких відповідей, що підкріплюються. Dw
Каспер Леон Нільсен

+1 для "Що мені не подобається, це те, що він робить налагодження налагодження функціонально іншим, ніж збірка версії. Якщо налагодження налагодження не вдається, але функціональність працює у випуску, то як це має сенс?" У .NET, System.Diagnostics.Trace.Assert()виконується як у складанні Release, так і у збірці налагодження.
DavidRR

7

Відповідно до стандарту IDesign , ви повинні

Стверджуйте кожне припущення. У середньому кожен п’ятий рядок - це твердження.

using System.Diagnostics;

object GetObject()
{...}

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

В якості відмови слід зазначити, що я не вважаю практичною реалізацію цієї IRL. Але це їх стандарт.


Схоже, Ювал Лоуї любить цитувати себе.
devlord

6

Використовуйте твердження лише в тих випадках, коли ви хочете, щоб чек видалено для версій версій. Пам'ятайте, що ваші твердження не спрацьовують, якщо ви не компілюєте в режимі налагодження.

З огляду на ваш приклад перевірки на нуль, якщо це в інтерфейсі лише API, я можу використати твердження. Якщо це в загальнодоступному API, я б точно використав явну перевірку і кинути.


У .NET можна System.Diagnostics.Trace.Assert()виконати твердження у складі релізу (виробництва).
DavidRR

Правило аналізу коду CA1062: Валідація аргументів публічних методів вимагає перевірки аргументу на те, nullколи: "Зовнішньо видимий метод відміняє один із своїх опорних аргументів, не перевіряючи, чи цей аргумент є нульовим ." У такій ситуації метод чи властивість повинні кинутись ArgumentNullException.
DavidRR

6

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

Debug.Assert(true);

Тому що це перевірка чогось, що ви вже припустили, що це правда. Наприклад:

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());
}

У вищесказаному є три різні підходи до нульових параметрів. Перший приймає це як допустиме (воно просто нічого не робить). Другий закидає виняток для обробки викликового коду (чи ні, що призводить до повідомлення про помилку). Третя припускає, що цього не може статися, і стверджує, що це так.

У першому випадку проблеми немає.

У другому випадку виникає проблема з кодом виклику - він не повинен був викликати GetFirstAndConsumenull, тому він отримує виняток.

У третьому випадку є проблема з цим кодом, оскільки він повинен був уже перевірити, що en != nullдо того, як його колись викликали, так що це неправда - це помилка. Або іншими словами, це повинен бути код, до якого теоретично можна було б оптимізувати Debug.Assert(true), sicne en != nullмає бути завжди true!


1
Отже, у третьому випадку, що відбувається, якщо en == nullу виробництві? Ви, можливо, говорите, що ніколи неen == null може статися у виробництві (оскільки програма була ретельно налагоджена)? Якщо так, то принаймні слугує альтернативою коментарю. Звичайно, якщо майбутні зміни будуть внесені, це також продовжує мати значення для виявлення можливої ​​регресії. Debug.Assert(en != null)
DavidRR

@DavidRR, я справді стверджую, що він ніколи не може бути нульовим, і так це твердження в коді, звідси і назва. Звичайно, я можу помилитися або помилитися зміною, і це значення виклику затвердження.
Джон Ханна

1
Виклики Debug.Assert()видаляються у версії Release. Отже, якщо ви перебуваєте не так, в третьому випадку , ви не будете знати його в виробництві (за умови використання збірки випуску у виробництві). Однак поведінка першого та другого випадків однакова в налагодженнях Debug and Release.
DavidRR

@DavidRR, що робить його доцільним лише тоді, коли я вважаю, що це не може статися, оскільки знову це твердження факту, а не перевірка стану. Звичайно, це також безглуздо, якщо мати твердження, мати помилку, яку він би спіймав, і все ж ніколи не потрапляв у цю справу при тестуванні.
Джон Ханна

4

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

1) Щось я не бачив тут, це додаткове концептуальне покриття, яке можуть надати Ассорти під час автоматизованого тестування . Як простий приклад:

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

Однак насправді введений тонкий недолік, але не виявлений в результатах тестування. У цьому випадку виклик став недетермінованим, і лише це забезпечує очікуваний результат. Або, можливо, це призвело до непоміченої помилки округлення. Або спричинила помилку, яка однаково компенсувалася в інших місцях. Або надано не лише запитуваний доступ, але й додаткові пільги, які не повинні надаватися. І т.д.

На цьому етапі висловлювання Debug.Assert (), що містяться у виклику, у поєднанні з новим випадком (або крайовим регістром), керованим одиничними тестами, можуть надати безцінне повідомлення під час тесту про те, що початкові припущення автора були недійсними, і код не повинен може бути звільнений без додаткового огляду. Свідчення з одиничними тестами є ідеальними партнерами.

2) Крім того, деякі тести просто написати, але дорогоцінні та непотрібні, враховуючи початкові припущення . Наприклад:

Якщо доступ до об'єкта доступний лише з певної захищеної точки входу, чи слід робити додатковий запит до бази даних мережевих прав з кожного методу об'єкта, щоб переконатися, що абонент має дозволи? Напевно ні. Можливо, ідеальне рішення включає кешування чи якесь інше розширення функцій, але дизайн цього не вимагає. Debug.Assert () негайно покаже, коли об’єкт було приєднано до небезпечної точки входу.

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

Припустимо, це вбудований пристрій у режимі реального часу. Викидання винятків та перезапуск, коли він стикається з неправильно сформованим пакетом, є контрпродуктивним. Натомість пристрій може скористатись найкращими зусиллями навіть до точки подачі шуму на виході. Він також може не мати людського інтерфейсу, пристрою реєстрації даних або навіть бути фізично доступним людині взагалі при його розгортанні в режимі випуску, а усвідомлення помилок найкраще забезпечується шляхом оцінки того ж виходу. У цьому випадку ліберальні твердження та ретельне тестування перед випуском є ​​більш цінними, ніж винятки.

4) Нарешті, деякі тести є непотрібними лише тому, що виклик сприймається як надзвичайно надійний . У більшості випадків, чим більше використовується багаторазовий код, тим більше зусиль докладено для того, щоб зробити його надійним. Тому загальним є Виняток для несподіваних параметрів від абонентів, але Assert для несподіваних результатів від callees. Наприклад:

Якщо основна String.Findоперація стверджує, що вона поверне a, -1коли критерії пошуку не знайдені, ви можете безпечно виконати одну операцію, а не три. Однак, якщо вона фактично повернулася -2, у вас може бути розумний хід дій. Недоцільно було б замінити більш простий обчислення на той, який тестується окремо на -1значення, і нерозумно в більшості середовищ випуску засмічувати ваш код тестами, які забезпечують, що основні бібліотеки працюють так, як очікувалося. У цьому випадку ідеали ідеальні.


4

Цитата, взята з Прагматичного програміста: Від мандрівника до майстра

Залиште твердження увімкнутими

Існує поширене непорозуміння щодо тверджень, оприлюднених людьми, які пишуть компілятори та мовне середовище. Це щось подібне:

Затвердження додають деякий накладний код. Оскільки вони перевіряють наявність речей, які ніколи не повинні відбутися, їх буде спрацьовувати лише помилка в коді. Після тестування та доставки коду вони більше не потрібні, і їх слід вимкнути, щоб швидше запустити код. Твердження - це засіб налагодження.

Тут є два явно помилкових припущення. По-перше, вони припускають, що тестування виявляє всі помилки. Насправді, для будь-якої складної програми ви навряд чи зможете перевірити навіть незначний відсоток перестановок, через які буде переданий ваш код (див. Безжальне тестування).

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

Вимкнення тверджень, коли ви постачаєте програму на виробництво, - це як пересікати високий провід без сітки, тому що ви колись це зробили на практиці . Це драматична цінність, але важко отримати страхування життя.

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


2

Ви завжди повинні використовувати другий підхід (кидання винятків).

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


1
Так, як і деякі інші відповіді тут: ви дійсно не розумієте різницю, тому ви відмовитесь від одного з пропозицій, встановивши помилкову дихотомію між ними в процесі. Dw
Каспер Леон Нільсен

3
Це єдина правильна відповідь у цьому списку IMO. Не відкидайте це так легко Каспер. Debug Assert - це анти-шаблон. Якщо його інваріант під час налагодження, його інваріант під час виконання. Якщо дозволити вашій програмі продовжувати роботу зі зламаним інваріантом, ви залишаєте вас у недетермінованому стані та потенційно гіршими проблемами, ніж збої. ІМО, краще мати однаковий код в обох складах, які швидко провалюються при розірваних контрактах, а потім реалізують надійну обробку помилок на найвищому рівні. наприклад, ізолювати компоненти та реалізувати можливість їх перезавантаження (наприклад, вкладка, що виходить з ладу в браузері, не збиває весь браузер).
Justin.m.chase

1
Тут може бути корисно включити Trace.Assert у свою дискусію, оскільки це не може бути відхилено тим самим аргументом.
Джон Кумбс

0

Ви повинні використовувати Debug.Assert для перевірки логічних помилок у ваших програмах. Компілятор може повідомити вас лише про синтаксичні помилки. Тож вам слід визначено використовувати оператори Assert для перевірки логічних помилок. Скажімо, тестуючи програму, яка продає автомобілі, на які тільки BMW синього кольору мають отримати 15% знижку. Компілятор не може нічого не сказати про те, якщо ваша програма логічно правильна у виконанні цього, але твердження твердження може.


2
Вибачте, але винятки роблять все те саме, тому ця відповідь не відповідає справжньому питанню.
Роман Старков

0

Я прочитав відповіді тут і подумав, що слід додати важливу відмінність. Існує два дуже різні способи використання тверджень. Один - це тимчасовий ярлик для розробника для "Це насправді не повинно статися, тому, якщо це не дасть мені знати, щоб я міг вирішити, що робити", на зразок умовної точки перерви, для випадків, коли ваша програма може продовжувати роботу. Інший - це спосіб занести припущення про дійсні стани програми у свій код.

У першому випадку твердження навіть не потрібно містити у кінцевому коді. Ви повинні використовувати 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>

-1

Я не знаю, як це в C # і .NET, але в C буде стверджувати (), працюватиме лише в тому випадку, якщо їх компілювати з -DDEBUG - ендузер ніколи не побачить assrt (), якщо він складений без. Це лише для розробника. Я використовую це дійсно часто, іноді простіше відслідковувати помилки.


-1

Я б не використовував їх у виробничому коді. Киньте винятки, ловіть і записуйте.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.