Почему попытка {...} наконец {...} хороша; попробуй {...} поймать {} плохо? - PullRequest
189 голосов
/ 24 сентября 2008

Я видел, как люди говорили, что использовать catch без аргументов - это плохо, особенно если этот catch ничего не делает:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

Однако это считается хорошей формой:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Насколько я могу судить, единственная разница между помещением кода очистки в блок finally и размещением кода очистки после блоков try..catch заключается в том, что в вашем блоке try есть операторы возврата (в этом случае код очистки). в конечном итоге будет работать, но код после попытки .. поймать не будет).

Иначе, что же такого особенного в конце концов?

Ответы [ 20 ]

2 голосов
/ 24 сентября 2008

Взято из: здесь

Возбуждение и отлов исключений не должны происходить обычно как часть успешного выполнения метода. При разработке библиотек классов клиентскому коду должна быть предоставлена ​​возможность проверить состояние ошибки, прежде чем предпринимать операцию, которая может привести к возникновению исключения. Например, System.IO.FileStream предоставляет свойство CanRead, которое можно проверить перед вызовом метода Read, предотвращая возникновение потенциального исключения, как показано в следующем фрагменте кода:

Dim str As Stream = GetStream () Если (str.CanRead), то код для чтения потока Конец, если

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

Чтобы проиллюстрировать влияние на производительность, которое может вызвать использование метода кодирования «выполнение до исключения», производительность приведения, который выдает InvalidCastException в случае сбоя, сравнивается с оператором C # as, который возвращает нули, если приведение выходит из строя. Выполнение этих двух приемов одинаково для случая, когда приведение является действительным (см. Тест 8.05), но для случая, когда приведение является недействительным, и использование преобразования приводит к исключению, использование преобразования в 600 раз медленнее, чем использование как оператор (см. Тест 8.06). Высокопроизводительное влияние метода выброса исключений включает в себя стоимость выделения, выброса и перехвата исключения, а также стоимость последующей сборки мусора объекта исключения, что означает, что мгновенное воздействие выброса исключения не столь велико. Поскольку выдается больше исключений, частая сборка мусора становится проблемой, поэтому общее влияние частого использования метода кодирования, генерирующего исключения, будет аналогично тесту 8.05.

2 голосов
/ 24 сентября 2008

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

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

2 голосов
/ 24 сентября 2008

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

2 голосов
/ 24 сентября 2008

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

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

1 голос
/ 24 сентября 2008

Ну, во-первых, это плохая практика - ловить исключения, с которыми вы не справляетесь. Прочтите Глава 5 о производительности .Net из Повышение производительности и масштабируемости приложений .NET . Заметьте, что вы, вероятно, должны загружать поток внутри блока try, таким образом, вы можете перехватить соответствующее исключение в случае сбоя. Создание потока вне блока try отрицательно сказывается на его цели.

1 голос
/ 24 сентября 2008

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

0 голосов
/ 25 сентября 2008

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

0 голосов
/ 24 сентября 2008

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

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

Если, например, вы должны будете поместить код в предложение 'catch' первого примера, который вызвал исключение (либо то, которое было инициировано, либо новое), код очистки считывателя никогда не будет выполнен. Наконец, выполняется независимо от того, что происходит в предложении 'catch'.

Итак, основное различие между «catch» и «finally» заключается в том, что содержимое блока «finally» (с несколькими редкими исключениями) можно считать гарантированным для выполнения, даже на лице непредвиденного исключения, в то время как любой код, следующий за предложением catch (но за пределами предложения finally), не несет такой гарантии.

Между прочим, Stream и StreamReader реализуют IDisposable и могут быть заключены в блок «using». Блоки 'Using' являются семантическим эквивалентом try / finally (без 'catch'), поэтому ваш пример можно выразить более кратко в виде:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

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

0 голосов
/ 24 сентября 2008

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

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

0 голосов
/ 24 сентября 2008

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

...