Коли кинути виняток?


435

У мене є винятки, створені для кожної умови, якої моя програма не очікує. UserNameNotValidException, І PasswordNotCorrectExceptionт.д.

Однак мені сказали, що я не повинен створювати винятки для цих умов. У моєму UML це винятки з основного потоку, тож чому він не повинен бути винятком?

Будь-які вказівки чи найкращі практики створення винятків?


58
Відкрийте, будь ласка, це дуже розумне та вагоме питання. Будь-яке питання передбачає певну думку, але в цьому випадку я підозрюю, що це питання "найкращої практики".
Тім Лонг

19
+1 для повторного відкриття. Як і багато інших цікавих тем "це залежить", і дуже корисно проаналізувати компроміси при прийнятті рішень. Те, що люди плутають думки з фактами у відповідях, це не заперечує. Просівання через грязь - вправа, яку слід залишити читачеві.
арон

12
Я також погоджуюся, що це питання слід повторно відкрити, оскільки воно пов'язане з передовою практикою. До речі, найкраща практика - це завжди думки, які можуть допомогти іншим.
Аджай Шарма

6
Microsoft каже: "Не повертайте коди помилок. Винятки є основним засобом повідомлення про помилки в рамках". і "... Якщо член не може успішно робити те, що призначений для цього, це слід вважати невдачею виконання та викинути виняток." msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75

4
Це може бути абсолютно розумним винятком, це просто залежить від того, які методи їх кидають. Викликаний метод IsCredentialsValid(username,password)не повинен кидати виняток, якщо ім'я користувача або пароль недійсні, але повертати помилково. Але скажімо, метод, який зчитує дані з бази даних, може законно викинути такий виняток, якщо аутентифікація не вдасться. Коротше кажучи: ви повинні кинути виняток, якщо метод не в змозі виконати завдання, яке він повинен зробити.
ЖакБ

Відповіді:


629

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

Приклад 1: скажіть, що у мене є функція, яка повинна перевірити довільний клас і повернути істину, якщо цей клас успадковується зі списку <>. Ця функція задає питання "Чи цей об'єкт є нащадком Списку?" Ця функція ніколи не повинна викидати виняток, оскільки в її роботі немає сірих областей - кожен клас або не успадковує з List <>, тому відповідь завжди є "так" або "ні".

Приклад 2: скажіть, що у мене є інша функція, яка вивчає Список <> і повертає значення true, якщо його довжина більше 50, і false, якщо довжина менша. Ця функція задає питання: "Чи є в цьому списку більше 50 позицій?" Але це питання робить припущення - воно передбачає, що об'єкт, який йому надається, є списком. Якщо я передаю це NULL, то це припущення є помилковим. У тому випадку, якщо функція повертає або вірно або невірно, то це порушення свої власні правила. Функція нічого не може повернути і стверджують, що вона відповіла правильно на питання. Тож не повертається - це викид.

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

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


15
Саме так! Виняток кидається, коли і лише тоді, коли порушені передумови функції (припущення про аргументи) !
Лайтман

11
У лінгвістиці це іноді називають провалом припущення . Класичний приклад пояснюється Бертран Расселом: "Чи лисий король Франції" не можна відповісти "так" чи "(відповідно." Король Франції лисий "не є ні правдою, ні помилкою), оскільки він містить помилкову припущення а саме, що є король Франції. Невдача припущення часто спостерігається з певними описами, і це часто при програмуванні. Наприклад, "У заголовку списку" є помилка припущення, коли список порожній, і тоді доречно викинути виняток.
Мохан

Це, можливо, найкраще пояснення!
gaurav

Дякую. Тому. Багато чого. "Чи лисий король Франції". Я чув це раніше, коли досліджував джунглі Мейнгона .... :) Дякую. @Mohan
ErlichBachman

285

Тому що це речі, які відбуватимуться нормально. Виняток не є механізмами управління потоком. Користувачі часто помиляються з паролями, це не винятковий випадок. Винятки мають бути справді рідкісними, UserHasDiedAtKeyboardтиповими ситуаціями.


3
Гм, ні. Винятки можуть використовуватися як механізми управління потоком, якщо не потрібна максимальна продуктивність, що стосується більшості веб-додатків. Python використовує виняток 'StopIteration' для припинення ітераторів, і він працює дуже добре. Вартість - це ніщо в порівнянні з IO тощо.
Seun Osewa

9
+1 відмінна відповідь. Мене настільки засмучують розробники, що працюють над API, що мені доводиться споживати і викидати винятки за кожну дрібницю. ДУЖЕ мало випадків дійсно вимагають винятків. Якщо у вас визначено 25 різних видів винятків, погляньте на свій дизайн ще раз, можливо, ви робите це неправильно.
7wp

1
чи повинен бути виняток, коли користувач намагається здійснити незаконні дії, які йому не дозволено, маніпулюючи кодом веб-сторінки, наприклад, видаляючи чужі повідомлення тут у StackOverflow?
Rajat Gupta

30
Винятки є механізмами управління потоком. Ви можете їх кинути. Ви можете їх зловити. Ви перемістили управління на інший фрагмент коду. Це контрольний потік. Єдина причина, що у мов взагалі є винятки, це те, що ви можете писати прямий код, не запитуючи "це щось просто не вдалося?" після всього, що ти робиш. Наприклад, Haskell не має винятків, оскільки монади та нотації можуть автоматизувати перевірку помилок для вас.
Джессі

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

67

На мої маленькі вказівки сильно впливає велика книга "Кодекс повний":

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

35

НЕ є винятком, якщо ім’я користувача недійсне або пароль невірний. Це те, чого слід очікувати при нормальному потоці роботи. Виняток становлять речі, які не є частиною звичайної роботи програми і є досить рідкісними.

EDIT: Я не люблю використовувати винятки, тому що ви не можете сказати, чи метод викидає виняток, просто подивившись на виклик. Ось чому винятки слід використовувати лише в тому випадку, якщо ви не можете гідно впоратися з ситуацією (подумайте, «не в пам’яті» або «комп’ютер загорівся»).


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

1
Перевірені винятки мають свій набір проблем. Я б все-таки скористався винятками "виняткових обставин", а не для речей, що входять до нормального робочого процесу.
EricSchaefer

2
У відповідь на вашу редакцію. Я завжди вкладаю в свої файли xml в кінці розділу підсумків винятки, які викидає функція, щоб я міг бачити цю інформацію в intellisense.
Меттью Вайнс

1
чи повинен бути виняток, коли користувач намагається здійснити незаконні дії, які йому не дозволено, маніпулюючи кодом веб-сторінки, наприклад, видаляючи чужі повідомлення тут у StackOverflow?
Rajat Gupta

1
Це здається, що ви говорите про помилки обробки (наша пам'ять, комп’ютер у вогні) та виключення з обробки коду (відсутня запис, недійсний тип введення тощо). Я думаю, що між ними є різниця.
Чак Берджесс

28

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


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

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

" відсутні файл на диску " Більшість мовних фреймворків, наприклад .NET Framework, також забезпечує API, щоб перевірити наявність файлів. Чому б не використати їх перед прямим доступом до файлу!
user1451111

23

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

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


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

7
Файл, що існує, не означає, що користувач може записувати у файл, наприклад. Перевірка на кожну можливу проблему справді стомлива і схильна до помилок. + ви дублюєте код (DRY).
Bjorn Reppen

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

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

Я думаю, що плутанина є результатом припущення, можливо, що передбачуваним результатом loginметоду -type буде те, що пароль може бути неправильним, він може бути фактично використаний для визначення цього, і не хотів би мати виняток у цьому випадку; тоді як у file openтиповому сценарії, який має бажаний конкретний результат - якщо система не в змозі надати результат через неправильні вхідні параметри або якийсь зовнішній фактор, це цілком логічне використання винятку.
theMayer

17

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

Це пов’язано з тим, як обробляються винятки, істинне неправильне введення та унікальні критичні елементи зупинки мають бути винятками, але не провальною інформацією про вхід.


14

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


Ви побачите безліч порад, в тому числі і у відповідях на це питання про те, що викидайте винятки лише у "виняткових" обставинах. Це здається поверхово розумним, але є хибною порадою, оскільки воно замінює одне запитання ("коли я повинен кинути виняток") іншим суб'єктивним питанням ("що є винятковим"). Натомість, дотримуйтесь порад Герба Саттера (для C ++, доступного у статті доктора Доббса Коли та як користуватися винятками , а також у його книзі з Андрієм Олександреску, Стандарти кодування C ++ ): киньте виняток, якщо і тільки якщо

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

Чому це краще? Чи не замінює це питання декількома питаннями про передумови, постумови та інваріанти? Це краще з кількох пов’язаних причин.

  • Передумови, постумови та інваріанти - це конструктивні характеристики нашої програми (її внутрішнього API), тоді як рішення throw- деталь реалізації. Це змушує нас мати на увазі, що ми повинні розглянути дизайн та його реалізацію окремо, а наша задача при впровадженні методу - виробляти щось, що відповідає дизайнерським обмеженням.
  • Це змушує нас мислити з точки зору передумов, посткондукцій та інваріантів, які є єдиними припущеннями, які повинні робити абоненти нашого методу, і висловлюються точно, забезпечуючи вільну зв'язок між компонентами нашої програми.
  • Тоді нещільне з’єднання дозволяє нам переробляти реалізацію, якщо це необхідно.
  • Пост-умови та інваріанти перевіряються; це призводить до коду, який можна легко перевірити одиницею, тому що після умови є предикатами наш код тестового модуля може перевірити (затвердити).
  • Мислення з точки зору пост-умов, природно, створює дизайн, який має успіх як пост-умова , що є природним стилем використання винятків. Нормальний шлях ("щасливого") виконання вашої програми викладений лінійно, і весь код обробки помилок переміщений до catchпунктів.

10

Я б сказав, що немає жорстких і швидких правил щодо використання винятків. Однак є вагомі причини їх використання чи їх використання:

Причини використання винятків:

  • Потік коду для загального випадку чіткіший
  • Може повернути складну інформацію про помилку як об'єкт (хоча це також можна досягти, використовуючи параметр "out", переданий посиланням)
  • Мови зазвичай надають певну функцію для керування акуратним очищенням у разі виключення (спробуйте / нарешті на Java, використовуючи C #, RAII у C ++)
  • Якщо виняток не викинуто, виконання іноді може бути швидшим, ніж перевірка повернених кодів
  • У Java перевірені винятки повинні бути оголошені або вилучені (хоча це може бути причиною проти)

Причини не використовувати винятки:

  • Іноді це надмірно, якщо обробка помилок проста
  • Якщо винятки не задокументовані та не задекларовані, вони можуть бути сприйняті кодом виклику, що може бути гірше, ніж якщо код виклику просто ігнорував код повернення (вихід програми проти тихої помилки - що гірше може залежати від сценарію)
  • У C ++ код, який використовує винятки, повинен бути безпечним для винятків (навіть якщо ви їх не кидаєте та не ловите, а посилаєте функцію метання побічно)
  • У C ++ важко сказати, коли функція може кинутись, тому ви повинні бути параноїком щодо безпеки виключень, якщо ви їх використовуєте
  • Кидання та вилучення винятків, як правило, значно дорожче порівняно з перевіркою зворотного прапора

Взагалі, я був би більш схильний використовувати винятки в Java, ніж у C ++ або C #, тому що я вважаю, що виняток, оголошений чи ні, є принципово частиною формального інтерфейсу функції, оскільки зміна вашої гарантії на виняток може перервати код виклику. Найбільшою перевагою їх використання в Java IMO є те, що ви знаєте, що ваш абонент повинен ОБРАЗУВАТИ виняток, і це покращує шанс правильної поведінки.

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

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


5

Класи виключень - це як "нормальні" класи. Ви створюєте новий клас, коли він "є" різного типу об'єкта, з різними полями та різними операціями.

Як правило, слід спробувати збалансувати кількість винятків та деталізацію винятків. Якщо ваш метод налічує більше 4-5 різних винятків, ви, ймовірно, можете об'єднати деякі з них у більш "загальні" винятки (наприклад, у вашому випадку "AuthenticationFailedException"), використовуючи повідомлення про виключення, щоб детально пояснити, що пішло не так. Якщо ваш код не обробляє кожен з них по-різному, вам не потрібно створювати багато класів винятків. І якщо це станеться, ви можете просто повернути перерахунок із помилкою, що сталася. Це трохи чистіше.


5

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

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


Мені дуже сподобався ваш коментар про винятки в циклі, і я подумав спробувати сам. Я написав зразкову програму, яка виконує цикл int.MaxValueразів і генерує в ній виняток "ділити на нуль". Версія IF / ELSE, в якій я перевіряв, чи дивіденд не дорівнює нулю до початку ділення , завершений за 6082 мс та 15407722 тиків, тоді як версія TRY / CATCH, в якій я генерував виняток і фіксувала виняток , завершена в 28174385 ms та 71371326155 тиків: цілих 4632 рази більше, ніж версія if / else.
user1451111

3

Я погоджуюся з способом japollock там - киньте схвалення, коли ви не впевнені в результаті операції. Дзвінки в API, доступ до файлових систем, дзвінки до бази даних тощо. У будь-який час ви переходите "межі" мов програмування.

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


3

правило кидання винятків досить просте. ви робите це, коли ваш код перейшов у стан НЕВЕРОЯТНЬОЇ НЕВЕРЛІДНОЇ якщо дані порушені або ви не можете повернути назад обробку, яка відбулася до цього моменту, потрібно припинити їх. дійсно що ще можна зробити? ваша логіка обробки з часом провалиться в іншому місці. якщо ви зможете якось відновитись, то зробіть це і не кидайте винятку.

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


2

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

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

Вони є "винятками" з основного потоку вашого UML, але є більш "гілками" в обробці.

Якщо ви спробували отримати доступ до свого файлу або бази даних passwd, але не змогли, це буде винятковим випадком і вимагатиме викидання виключення.


" Якщо ви спробували отримати доступ до файлу або бази даних passwd, але це не вдалося, це буде винятковим випадком і вимагає викидання виключення. " Більшість мовних фреймворків, наприклад .NET Framework, також забезпечує API для перевірки наявності файлів. Чому б не використати їх перед прямим доступом до файлу!
user1451111

2

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

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


2

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


2

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


2

Я б сказав, що взагалі кожен фундаменталізм призводить до пекла.

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

Я зазвичай вважаю за краще створити два основні типи винятків, які використовуються у всій системі: LogicalException та TechnicalException . Вони можуть бути додатково розмежовані за підтипами, якщо це необхідно, але це, як правило, не є необхідним.

Технічний виняток позначає дійсно несподіваний виняток, наприклад, коли сервер бази даних не працює, з'єднання з веб-службою кинуло IOException тощо.

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

Зверніть увагу, що навіть логічний виняток не призначений для регулярного використання для управління потоком програми, а для того, щоб виділити ситуацію, коли потік справді повинен закінчитися. При використанні в Java обидва типи винятків - це підкласи RuntimeException, а обробка помилок орієнтована на аспекти.

Тож у прикладі входу може бути розумним створити щось на зразок AuthenticationException та розрізнити конкретні ситуації за значеннями перерахувань, такими як UsernameNotExisting , PasswordMismatch і т.д. . Ви також можете легко використовувати якийсь загальний механізм обробки винятків, оскільки ви маєте категоризовані винятки і добре знаєте, що потрібно розповсюджувати до користувача та як.

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

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


2

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


1

Основна причина уникнення кидання винятку полягає в тому, що велика кількість накладних витрат пов'язана з киданням виключення.

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

Неправильне ім’я користувача - це не обов'язково помилка програми, а помилка користувача ...

Ось гідна відправна точка для виключень у .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx


1

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

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


1

У мене є три типи умов, які я ловлю.

  1. Неправильний або відсутні дані не повинні бути винятком. Використовуйте як клієнтський js, так і регекс на стороні сервера, щоб виявити, встановити атрибути і переслати назад на ту саму сторінку з повідомленнями.

  2. AppException Зазвичай це виняток, який ви виявляєте та кидаєте у свій код. Іншими словами, це очікувані вами файли (файл не існує). Увійдіть у систему, встановіть повідомлення та перейдіть на сторінку загальної помилки. На цій сторінці зазвичай є трохи інформації про те, що сталося.

  3. Несподіваний виняток. Це ті, про кого ти не знаєш. Увійдіть до нього з деталями та перешліть їх на загальну сторінку помилок.

Сподіваюсь, це допомагає


1

Безпека пов'язана з вашим прикладом: не слід повідомляти зловмиснику, що ім’я користувача існує, але пароль невірний. Це додаткова інформація, яку вам не потрібно ділитися. Просто скажіть, що "ім'я користувача або пароль невірні".


1

Проста відповідь - коли операція неможлива (через будь-яке застосування АБО, тому що це порушить бізнес-логіку). Якщо метод викликається і неможливо зробити те, про що було написано, киньте виняток. Хороший приклад - конструктори завжди викидають ArgumentExceptions, якщо екземпляр не може бути створений за допомогою наданих параметрів. Інший приклад - InvalidOperationException, який викидається, коли операцію неможливо виконати через стан іншого члена або членів класу.

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

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

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

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

Ось приклад уникнення використання винятків у сценарії, як щойно описаний, за допомогою шаблону Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

1

На мій погляд, основним питанням має бути те, чи можна було б сподіватися, що абонент захоче продовжувати нормальний потік програми, якщо виникає умова. Якщо ви не знаєте, чи є окремі методи doSomething і trySomething, де перший повертає помилку, а другий - ні, або є програма, яка приймає параметр, який вказує, чи слід викидати виняток, якщо він не працює). Розглянемо клас для надсилання команд у віддалену систему та повідомлення про відповіді. Окремі команди (наприклад, перезапуск) призведуть до того, що віддалена система надсилатиме відповідь, але потім не реагує на певний проміжок часу. Таким чином, корисно мати можливість відправляти команду "ping" і з'ясовувати, чи віддалена система відповідає протягом розумного проміжку часу, не викидаючи виняток, якщо це не так " t (абонент, ймовірно, очікував, що перші кілька спроб "пінг" не зможуть, але одна з них спрацює). З іншого боку, якщо у вас є послідовність команд типу:

  exchange_command ("відкрити тимчасовий файл");
  exchange_command ("записувати дані темпфілів {будь-які}");
  exchange_command ("записувати дані темпфілів {будь-які}");
  exchange_command ("записувати дані темпфілів {будь-які}");
  exchange_command ("записувати дані темпфілів {будь-які}");
  exchange_command ("закрити тимплейф");
  exchange_command ("скопіюйте тимчасовий файл в realfile");

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

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


1

"PasswordNotCorrectException" не є хорошим прикладом використання винятків. Користувачів, які неправильно отримують свої паролі, слід очікувати, тому навряд чи IMHO є винятком. Ви, ймовірно, навіть відновлюєтесь після цього, показуючи приємне повідомлення про помилку, тож це просто перевірка на дійсність.

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

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

Отже, як правило:

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


0

Ви можете використовувати дещо загальні винятки для цих умов. Наприклад, наприклад, ArgumentException призначений для використання, коли що-небудь піде не так з параметрами методу (за винятком ArgumentNullException). Як правило, вам не знадобляться винятки, такі як LessThanZeroException, NotPrimeNumberException тощо. Подумайте про користувача вашого методу. Кількість умов, з якими вона захоче конкретно впоратися, дорівнює кількості винятків, які повинен викинути ваш метод. Таким чином, ви можете визначити, наскільки детальні винятки у вас будуть.

До речі, завжди намагайтеся надати певні способи для користувачів ваших бібліотек, щоб уникнути винятків. TryParse - хороший приклад, він існує так, що вам не доведеться використовувати int.Parse та ловити виняток. У вашому випадку ви можете надати деякі методи, щоб перевірити, чи ім’я користувача дійсне чи пароль правильний, щоб вашим користувачам (або вам) не доведеться робити багато оброблень виключень. Це, сподіваємось, призведе до більш читаного коду та кращої продуктивності.


0

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

  • Хто називає ваш код? Це якийсь публічний API чи внутрішня бібліотека?
  • Якою мовою ви користуєтесь? Наприклад, якщо мова йде про Java, то викидання (перевіреного) винятку покладає явний тягар на ваш абонент, щоб якось обробляти цю умову помилки, на відміну від статусу повернення, який можна ігнорувати. Це може бути добре чи погано.
  • Як обробляються інші умови помилок у тій самій програмі? Абоненти не хочуть мати справу з модулем, який обробляє помилки ідіосинкратичним способом на відміну від усього іншого в системі.
  • Скільки речей може піти не так у відповідну рутину, і як з ними можна поводитися по-різному? Розглянемо різницю між низкою блоків вилову, які обробляють різні помилки, і перемиканням коду помилки.
  • Чи маєте ви структуровану інформацію про помилку, яку потрібно повернути? Якщо викинути виняток, ви зможете розмістити цю інформацію, ніж просто повернути статус.

0

Є два основні класи винятку:

1) Виняток системи (наприклад, втрачено з'єднання з базою даних) або 2) Виняток користувача. (наприклад, перевірка введення користувачем, "пароль невірний")

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

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

0

Деякі корисні речі, про які варто подумати, вирішуючи, чи є виняток:

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

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

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