Чи завжди добре мати порожню заяву про вилов?


58

Я подумав над цим і не міг придумати приклад. Чому хтось хотів би зловити виняток і нічого не робити з цим? Чи можете ви навести приклад? Можливо, це просто щось, чого ніколи не слід робити.


Що зрештою?
Кріс

2
btw - про це запитали в минулому році: stackoverflow.com/questions/1234343/…
warren

17
він повинен хоча б містити коментар, чому він порожній.
peterchen

Відповіді:


77

Я роблю це постійно з такими речами, як помилки конверсії в D:

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

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

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


15
+1 для коментаря пояснення.
Майкл К

Деякі IDE дозволяють вказати, що певне ім'я для винятку (як правило, "ігнорувати") вказує на те, що ви навмисно його ігноруєте, що пригнічує попередження, яке воно в іншому випадку відображатиметься; це займає місце коментаря.
Алекс Фейнман

Я не знайомий D, але я припускаю, що можна зробити щось на зразок bool TryParse (рядок рядка, з подвійного valToParse), що може бути чистішим.
ysolik

4
Ого, хтось, хто насправді використовує D! :-P
Джеймс Мак-Нілліс

4
Як каже @Michael - це не зовсім порожньо, оскільки ви отримали коментар; слід обов'язково додати коментар "Навмисно залишено цей конт", якщо немає заяви
STW

50

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

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

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

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


10
чудовий момент. налагодження коду налагодження - це завжди завдання.
DevSolo

7
Це, мільйон разів. Особливо, якщо врахувати, що якщо ви ведете журнал, ви можете опинитися в жахливому стані; у вас може бути поза пам'яттю, може бути збій, може бути що завгодно. У цей момент, коли ви реєструєте помилку, а ЦЕ не вдається, єдине відповідальне, що потрібно зробити, - це нічого; у вас немає гарантії, що все, що ви спробуєте, не погіршиться.
GWLlosa

Чудова точка. У своєму коді реєстрації я також хочу додати InnerMessage для виключення. Багато разів це недійсне значення, тому намагаючись додати, це підірве саму реєстрацію.
Тангурена

@Tangurena Якщо це часто нульово, тоді впорайтеся з цим, не дуйте. У C # я часто охороняю такі речі ?? "";
Лорен Печтел

8

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

Якщо ви це робите, додайте коментар. Завжди коментуйте все, що схоже на помилку (провал у switchвиписці, порожній catchблок, присвоєння умові).

Ви можете стверджувати, що це не належне використання винятків, але це вже інше питання.


6

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

Як наслідок цього підходу, коли ви програмуєте один конкретний рівень програми, у вас є кілька варіантів:

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

Це насправді не залишає місця для порожніх catchблоків.

ВИДАЛЕНО ДО ДОДАТИ : припустимо, ви програмуєте мовою, де викидання виключень - це звичайний спосіб контролювати логіку програми (одна з альтернатив goto). Тоді порожній catchблок у програмі, написаній цією мовою, дуже схожий на порожній elseблок традиційною мовою (або зовсім не elseблокуючий). Однак я вважаю, що це не стиль програмування, рекомендований спільнотами C #, Java або C ++.


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

+1 за визнання того, що порожній блок лову може означати використання винятків для контролю потоку.
Джон М Гант

Використання винятків для логіки програмування, як правило, вважається поганою практикою. Але, на диво (мої оригінальні аргументи проти винятків) винятки не спричиняють помітного успіху. Дивіться codeproject.com/KB/exception/ExceptionPerformance.aspx .
Еван Плейс

6

У деяких випадках Java вимагає, щоб ви обробляли виняток, який, за абсолютно жодних обставин, ніколи не може відбутися. Розглянемо цей код:

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Кожна реалізація платформи Java необхідна для підтримки наступних стандартних діаграм.

...

UTF-8

Не порушуючи вашу платформу Java, просто не існує способу викликати цей виняток. Так навіщо турбуватись з цим? Але ви не можете просто пропустити статтю про випробування.


1
У таких випадках я б спокусився додати throw new Error(e)просто, щоб бути абсолютно впевненим, що я нічого не пропустив (або повідомив про справді розбиту платформу).
Галле Кнаст

@HalleKnast Так. Про це ми говорили тут докладно: softwareengineering.stackexchange.com/questions/122233/…
користувач281377

5

Як правило, ні. Ви або хочете викинути виняток у стек, або записувати те, що сталося.

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

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;

3

У деяких випадках виняток зовсім не є винятковим; насправді це очікується. Розглянемо цей приклад:

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

Оскільки в java.lang.Process () немає методу isAlive (), про єдиний спосіб перевірити, чи він ще живий, є виклик exitValue (), який викидає IllegalThreadStateException, якщо процес все ще працює.


2

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

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

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

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


2

IMO є одне місце, де порожні заяви про вилов. У тестових випадках, коли ви очікуєте винятку:

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}

+1, я ненавиджу те, що я роблю це , але вона працює в JUnit
вулиці

@sal: а як щодо @Test (очікуваний = Exception.class)?
ysolik

@ysolik, погана звичка
сал

1
@sal: Чому це шкідлива звичка?
Адам Лір

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

2

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


1

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

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

Якщо це доброякісне виняток, то я телефоную на метод .Debug () мого Logger; інакше Logger.Error () (і (можливо) кинути).

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

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


Що робити, якщо _log.Debug кидає виняток (з будь-якої причини)?
JohnL

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

1

Перевірка наявності є гарним випадком використання:

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

Інший випадок, коли використовується finallyпункт:

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

Існує пропозиція дозволити пропущення обов'язкового вилову в ECMAScript, що стосується цього та подібних випадків використання.

Список літератури


Інший приклад: у nodejs метод fs.exists (який повертає істинне чи помилкове) застарілий ( nodejs.org/dist/latest-v10.x/docs/api/… ) на користь fs.access, який видаляє виняток у у випадку, якщо файл не існує. Якщо ви хочете щось зробити лише за наявності файлу, пункт лову зовсім не потрібний.
системович

0

Єдиний раз, коли мені справді потрібно було зробити щось подібне, це був клас реєстрації для консольного додатка. Був глобальний обробник лову верхнього рівня, який видав помилку STDOUT, записав помилку у файл, а потім закрив програму кодом помилки> 0.

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

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

Там, звичайно, є коментар, який пояснює, чому це порожньо.

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


0

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

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


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

0

Вимкнення системних ресурсів витончено, щоб відновитись після помилки

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

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

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

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