Пустые блоки блокировки

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

Недавно я прочитал эту статью о подобной вещи: http://c2.com/cgi/wiki?EmptyCatchClause

Этот человек говорит о том, как комментарий

// should never occur 

является запахом кода и никогда не должен появляться в коде. Затем они объясняют, как комментарий

// don't care if it happens

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

var addressCollection = new MailAddressCollection();
foreach (string address in addresses)
{
    try
    {
        addressCollection.Add(address);
    }
    catch (Exception)
    {
        // Do nothing - if an invalid email occurs continue and try to add the rest
    }
}

Теперь вы можете подумать, что это плохая идея, так как вы хотите вернуться к пользователю и объяснить, что одно или несколько сообщений не могут быть отправлены получателю. Но что, если это просто адрес CC? Это менее важно, и вы все равно можете отправить сообщение в любом случае, даже если один из этих адресов недействителен (возможно, только опечатка).

Я правильно использую пустой блок catch или есть лучшая альтернатива, о которой я не знаю?

Ответ 1

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

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

Для второго (ошибка программирования) было бы большой ошибкой просто "усвоить" исключение. Лучше всего сделать запись журнала стека, а затем вывести сообщение об ошибке, сообщающее пользователю о внутренней ошибке, и что они должны отправить свои журналы обратно разработчикам (т.е. Вы). Или, хотя вы разрабатываете, вы можете просто распечатать трассировку стека на консоль и свернуть программу.

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

В общем:

Хорошо, что вы читаете технические книги и статьи. Вы можете многому научиться в этом. Но, пожалуйста, помните, как вы читаете, вы найдете много советов от людей, говорящих, что делать такое-то дело всегда неправильно или всегда правильно. Часто эти мнения граничат с религией. НИКОГДА не верьте, что делать вещи определенным образом абсолютно "правильно", потому что книга или статья (или ответ на SO... <cough> ) сказали вам об этом. Есть исключения из каждого правила, и люди, которые пишут эти статьи, не знают подробностей вашего приложения. Вы делаете. Убедитесь, что то, что вы читаете, имеет смысл, а если нет, доверьтесь себе.

Ответ 2

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

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

Разница между "не должна произойти" и "не волнует, если это произойдет" заключается в том, что если это "не должно произойти", тогда, когда это произойдет, вы не захотите усвоить ее молча! Если это условие, которое вы никогда не ожидали, вам обычно нужно, чтобы ваше приложение вышло из строя (или, по крайней мере, завершило чистоту и зарегистрировало все то, что произошло), чтобы вы могли идентифицировать это невозможное условие.

Ответ 3

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

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

foreach (string address in addresses)
{
    try
    {
        addressCollection.Add(address);
    }
    catch (EmailNotSentException ex)
    {
        if (IsCausedByMissingCcAddress(ex))
        {
            // Handle this case here e.g. display a warning or just nothing
        }
        else
        {
            throw;
        }
    }
}

Обратите внимание, что приведенный выше код улавливает определенные (если вымышленные) исключения, а не ловит Exception. Я могу думать о очень немногих случаях, когда законно ловить Exception, а не улавливать определенный тип исключения, который вы ожидаете получить.

Ответ 4

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

Часто эти методы будут иметь префикс Try перед ними. Вместо того, чтобы бросать исключение, функция возвращает логическое значение, указывающее, удалось ли выполнить задачу.

Хорошим примером этого является Parse vs TryParse

string s = "Potato";
int i;
if(int.TryParse(s, out i))
{
    //This code is only executed if "s" was parsed succesfully.
    aCollectionOfInts.Add(i);
}

Если вы попробуете вышеуказанную функцию в цикле и сравните ее с ее уравновешиванием Parse + Catch, метод TryParse будет намного быстрее.

Ответ 5

Использование пустого блока catch просто проглатывает Exception, я всегда буду обращаться с ним, даже если он сообщает вам, что произошел Exception.

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