Чому порожні блоки вилову є поганою ідеєю? [зачинено]


194

Я щойно бачив запитання про те , що люди (в тому числі Джон Скіт) кажуть, що порожні блоки лову - це дійсно погана ідея? Чому це? Хіба немає ситуації, коли порожній вилов не є неправильним дизайнерським рішенням?

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


53
Я б написав коментар, пояснюючи, чому він повинен бути порожнім.
Мехрдад Афшарі

5
... або принаймні журнал, що його спіймали.
мат б

2
@ocdecio: уникайте його для коду очищення , не уникайте його взагалі.
Джон Сондерс

1
@ocdecio: Ще один випадок уникати використання try..catch - це коли ви ловите винятки для відомих типів збоїв. наприклад, числові винятки для конверсій
MPritchard,

1
@ocdecio - спробуйте .., нарешті, краще, ніж спробувати .. порожній вилов, оскільки помилка продовжує стек - отримати або обробляти рамки, або вбити програму і відображатись користувачеві - обидва результати кращі, ніж " мовчазна невдача ».
Нейт

Відповіді:


298

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

Це еквівалент програмування розміщення чорної стрічки над попереджувальним світлом двигуна.

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


4
У тих по-справжньому рідкісних обставинах, коли мені справді не потрібні винятки, а журнал чомусь недоступний, я обов'язково коментую, що це навмисно - і чому він не потрібен, і ще раз зазначу, що я все-таки вважаю за краще це ввійти, якщо це було можливо в тому середовищі. В основному, я зауважую, що БІЛЬШЕ роботи, ніж ведення журналу, якби це було варіантом у цій обставині. На щастя, у мене є 2 клієнти, для яких це питання, і це лише при надсиланні некритичних електронних листів з їх веб-сайтів.
Джон Руді

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

3
Як і будь-який відхід від прийнятої практики, зробіть це, якщо потрібно, і задокументуйте це, щоб наступний хлопець знав, що ви зробили і чому.
Девід Торнлі

5
@Jason: як мінімум, ви повинні включити докладний коментар, який пояснює, чому ви замовчуєте винятки.
Нед Батчелдер,

1
Ця відповідь передбачає дві речі: 1. Що виняток, який викидають, є справді значимим і той, хто написав код, який кидає, знав, що він робить; 2. Цей виняток справді є "умовою помилки" і не зловживається як контрольний потік (хоча це більш конкретний випадок мого першого пункту).
Борис Б.

38

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

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


11

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

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

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


6
Ви, звичайно, надмірно узагальнюєте. Тільки тому, що вам це не потрібно, це не означає, що ніхто цього не робить. Навіть якщо абсолютно нічого не можна зробити у відповідь, десь є хтось із вимогою збирати статистику щодо покинутих з'єднань. Тому досить грубо вважати, що "програміст бібліотеки не знає, що він робить".
Ben Voigt

8

Є рідкісні випадки, коли це можна виправдати. У Python ви часто бачите подібну конструкцію:

try:
    result = foo()
except ValueError:
    result = None

Тож може бути добре (залежно від вашої заявки):

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

У недавньому проекті .NET мені довелося написати код для перерахування DLL-плагінів для пошуку класів, що реалізують певний інтерфейс. Відповідний біт коду (у VB.NET, вибачте):

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

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


Я назвав log4net одним із таких випадків, перш ніж побачив вашу відповідь.
Скотт Лоуренс

7

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

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


1
+1 Я наголошу на "або" в цьому останньому реченні для додаткового впливу
MPritchard,

Цей другий абзац може залежати від мови, правда?
David Z

3
якщо ви , звичайно , не говоримо про IE6 або IE7 ;-) ... де улов IS потрібно або , нарешті , не виконує. (це було зафіксовано в IE8 btw)
scunliffe


Дуже правильно. Можливо, варто виділити мови. Я вважаю .net добре
MPritchard

7

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

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


3
Іноді ви знаєте, що це ні на що не вплине. Тоді продовжуйте робити це і прокоментуйте це, щоб наступний хлопець не просто подумав, що ви накрутили, бо ви некомпетентний.
Девід Торнлі

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

2
@David Thornley: Як ти знаєш, що це не вплине на щось, якщо ти хоча б не перевіриш виняток, щоб визначити, чи це очікувана і безглузда помилка чи помилка, яку слід замість цього поширювати вгору?
Дейв Шерохман

2
@Dave: Хоча catch (Exception) {}це погана ідея, це catch (SpecificExceptionType) {}може бути прекрасно. Програміст DID перевіряє виняток, використовуючи інформацію про тип у пункті вилучення.
Ben Voigt

7

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

Але навіть у цьому випадку повідомлення про налагодження може бути в порядку.


6

За Джош Блох - Пункт 65: Чи не ігнорувати виключення з Effective Java :

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

3

Порожній блок лову по суті говорить: "Я не хочу знати, які помилки кидаються, я просто збираюся їх ігнорувати".

Це схоже на VB6 On Error Resume Next, за винятком того, що все, що знаходиться в пробному блоці після викиду винятку, буде пропущено.

Що не допомагає, коли щось потім ламається.


Насправді я б сказав, що "On Error Resume Next" - це гірше - він просто спробує наступний рядок незалежно. Порожній улов підскочить, щоб пройти заключну
діаграму

Гарна думка. Я, мабуть, мушу зазначити, що у своїй відповіді.
Powerlord

1
Це не зовсім так, якщо ви маєте порожню спробу ... ловити з певним винятком винятків, то він буде ігнорувати саме цей тип винятків, і це може бути законно ...
Meta-Knight

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

@Scott: Деякі мови (наприклад, Java) перевірили винятки ... винятки, для яких ви змушені писати блоки лову.
Powerlord

3

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


2

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


1

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


1

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


1

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


1

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

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}

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

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

Я спочатку думав про COMExceptions. Ви можете перевірити наявність конкретного ErrorCode та обробити очікувані коди. Я роблю це часто під час програмування з ArcOjbects.
xanadont

2
-1 Прийняття рішень у коді на основі повідомлення про виключення - це дуже погана ідея. Точне повідомлення рідко задокументоване і може змінитися в будь-який час; це деталізація реалізації. Або повідомлення може базуватися на локалізованому рядку ресурсу. У будь-якому випадку це має бути прочитане лише людиною.
Вім Коен

Еек. Виняток. Масаж може бути локалізованим і, таким чином, не тим, що ви очікуєте. Це просто неправильно.
франкхоммери

1

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

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

Інший приклад: CLR 2.0 змінив спосіб поводження з необробленими винятками в потоці фіналізатора. До початку 2.0 було дозволено пережити цей сценарій. У поточному CLR процес припиняється у випадку незробленого винятку на потоці фіналізатора.

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


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

Отже, ідучи зі своїм прикладом, це погана ідея в такому випадку, тому що ви ловите і ігноруєте всі винятки. Якби ви ловили лише EInfoFromIrrelevantSourceNotAvailableі ігнорували це, це було б добре, але це не ви. Ви також ігноруєте ENetworkIsDown, що може бути або не бути важливим. Ви ігноруєте ENetworkCardHasMeltedі EFPUHasDecidedThatOnePlusOneIsSeventeen, що майже напевно важливо.

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


1

Бувають ситуації, коли ви можете їх використовувати, але вони повинні бути дуже рідкісними. Ситуації, в яких я можу використовувати один, включають:

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

  • циклічні технічні ситуації, такі як візуалізація чи обробка звуку або зворотний виклик списку, де сама поведінка продемонструє проблему, кидання винятку просто перешкодить, а реєстрація винятку, ймовірно, призведе до 1000-ти повідомлень "не вдалося XXX" .

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

Для більшості додатків winforms я виявив, що достатньо мати одну заяву спробу для кожного вводу користувача. Я використовую такі методи: (AlertBox - це лише швидка обгортка MessageBox.Show)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Тоді кожен обробник подій може зробити щось на кшталт:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

або

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

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


1

Якщо ви не знаєте, що робити в блоці ловлі, ви можете просто записати цей виняток, але не залишайте його порожнім.

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }

Чому це було знято?
Vino

0

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

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