Это когда-нибудь нормально иметь пустой оператор catch?


58

Я думал об этом и не смог придумать пример. Почему кто-то хочет поймать исключение и ничего не делать с этим? Можете привести пример? Может быть, это просто то, что никогда не должно быть сделано.


Как насчет наконец?
Крис

2
Кстати - об этом спрашивали на SO в прошлом году: stackoverflow.com/questions/1234343/…
Уоррен

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 должны содержать что-то , даже если это просто комментарий, объясняющий, почему вы игнорируете исключение.

Изменить: Я также хочу подчеркнуть, что, если вы собираетесь сделать что-то подобное, вы должны быть осторожны, чтобы поймать только конкретное исключение, которое вы хотите игнорировать. Делать старый catch {}добрый почти всегда плохо.


15
+1 за пояснительный комментарий.
Майкл К

Некоторые IDE позволяют вам указать, что конкретное имя для исключения (обычно «игнорировать») указывает на то, что вы намеренно его игнорируете, что подавляет предупреждение, которое в противном случае показывалось бы; это занимает место комментария.
Алекс Фейнман

Я не знаком с D, но я предполагаю, что возможно сделать что-то вроде bool TryParse (string line, out double valToParse), что может быть чище.
ysolik

4
Вау, тот, кто на самом деле использует D! :-P
Джеймс МакНеллис

4
Как говорит @Michael - он не совсем пустой, так как у вас там есть комментарий; должно быть обязательно добавить комментарий «этот уловок намеренно оставлен пустым», если нет заявления
STW

50

Один пример, где я думаю, что можно просто проглотить исключение, ничего не делая, даже регистрируя исключение, находится внутри самого кода регистрации.

Если вы пытались что-то зарегистрировать и получили исключение, вы ничего не можете с этим поделать:

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

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


10
отличный момент. отладка кода отладки всегда является проблемой.
DevSolo

7
Это миллион раз. Особенно, если учесть, что если вы регистрируетесь, вы можете быть в ужасном состоянии; у вас может быть нехватка памяти, может произойти сбой, может быть что угодно. На этом этапе, когда вы регистрируете ошибку, и ЭТО терпит неудачу, единственное, что нужно сделать, это ничего не делать; у вас нет гарантии, что все, что вы попробуете, не ухудшит ситуацию
GWLlosa

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

@Tangurena Если это часто ноль, тогда справляйся, не дуй. В C # я часто охраняю такие вещи "";
Лорен Печтел

8

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

Если вы делаете это, добавьте комментарий. Всегда комментируйте все, что похоже на ошибку (падение в switchоператоре, пустой catchблок, назначение в условии).

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


6

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

Как следствие этого подхода, когда вы программируете один конкретный прикладной уровень, у вас есть несколько вариантов:

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

Это действительно не оставляет места для ничего не делать, пустые catchблоки.

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


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

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

Использование исключений для программирования логики обычно считается плохой практикой. Но, как ни странно (мой первоначальный аргумент против исключений) исключения не вызывают заметного снижения производительности. См. Codeproject.com/KB/exception/ExceptionPerformance.aspx .
Эван Плейс

6

В некоторых случаях Java требует от вас обработки исключения, которое ни при каких обстоятельствах не может произойти. Рассмотрим этот код:

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

Каждая реализация платформы Java должна поддерживать следующие стандартные кодировки.

...

UTF-8,

Не нарушая вашу платформу Java, просто невозможно вызвать это исключение. Так зачем заниматься этим? Но вы не можете просто опустить предложение try-catch.


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

@HalleKnast Да. Это было подробно обсуждено здесь: softwareengineering.stackexchange.com/questions/122233/…
user281377

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

ИМО есть одно место, где пустые операторы catch в порядке. В тестовых случаях, когда вы ожидаете исключения:

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

@sal: как насчет @Test (ожидается = Exception.class)?
ysolik

@ysolik, вредная привычка
sal

1
@sal: Почему это плохая привычка?
Адам Лир

Ummmm. Есть способы сделать это с помощью модульного тестирования. Ex. NUnit. Есть определенные случаи, когда тестирование, которое предсказуемо терпит неудачу, может быть полезным. Хотя я бы никогда не сделал это в рабочем коде.
Эван Плейс

2

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


1

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

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

Если это мягкое исключение, то я вызываю метод моего Logger .Debug (); в противном случае Logger.Error () (и (возможно) throw).

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

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


Что если _log.Debug выдает исключение (по какой-либо причине)?
JohnL

Затем .Debug () ест исключение, и я никогда не знаю об этом. Но я чувствую себя гораздо более уверенно в своем регистраторе, чем в коде, который часто генерирует исключения. Если бы я беспокоился об этом, я бы мог реализовать какой-то шаблон, который следит за входом в блок catch и сохраняет его для дальнейшего использования. В любом случае - точка взята
Тим Класон

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 (который возвращает true или false) не рекомендуется ( nodejs.org/dist/latest-v10.x/docs/api/… ) в пользу fs.access, который вызывает исключение в если файл не существует Если кто-то хочет сделать что-то, только если файл существует, предложение catch совершенно не нужно.
Системович

0

Единственный раз, когда мне действительно нужно было сделать что-то подобное, было с классом журналирования для консольного приложения. Был глобальный обработчик верхнего уровня, который отправлял ошибку в STDOUT, записывал ошибку в файл, а затем закрывал приложение с кодом ошибки> 0.

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

Если вы отбросите некоторые из блоков try {}, вы можете получить исключение, появившееся в окне сообщения (а не то, что вы хотите для приложения конфигурирования без вывода сообщений). Так что в том методе, который записывает в файл журнала, у меня есть пустой обработчик catch. Это не скрывает ошибку, поскольку вы по-прежнему получаете код ошибки> 0, она просто останавливает бесконечный цикл и позволяет приложению корректно завершить работу.

Конечно, есть комментарий, объясняющий, почему он пуст.

(Я собирался опубликовать это раньше, я просто боялся быть в забвении. Хотя я не единственный, хотя ...)


0

Это нормально в ситуации, когда какая-то последовательность должна быть выполнена независимо от того, какая часть критична в конце.

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


Это не значит, что вы ничего не делаете, просто то, что вы делаете, не прерывает поток. Поймайте ваши исключения, сохраните их в памяти (без задержки записи на диск), а затем отключите питание в операторе finally. Затем делайте что хотите с сохраненной информацией.
Лорен Печтел

0

Изящное закрытие системных ресурсов для восстановления после ошибки

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.