Почему попытка {...} наконец {...} хорошая; try {...} catch {} bad?

Я видел, как люди говорят, что плохая форма использовать 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 (в этом случае код очистки в конце будет работать, но код после try..catch не будет).

В противном случае, что особенно важно в конце?

Ответ 1

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

Ответ 2

"Наконец" - это утверждение "Что-то, что вы всегда должны делать, чтобы убедиться, что состояние программы нормальное". Как таковая, всегда хорошая форма иметь ее, если есть вероятность, что исключения могут сбросить состояние программы. Компилятор также подходит к концу, чтобы убедиться, что ваш код finally запущен.

"Catch" - это утверждение "Я могу исправить это исключение". Вы должны только восстанавливаться из-за исключений, которые вы действительно можете исправить - поймать без аргументов говорит: "Эй, я могу оправиться от всего!", Что почти всегда неверно.

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

Ответ 3

Потому что, когда эта одна строка генерирует исключение, вы не знаете этого.

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

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

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

Ответ 4

Наконец выполняется независимо от того, что. Итак, если ваш блок try был успешным, он выполнит, если ваш блок try завершится неудачно, он затем выполнит блок catch и затем блок finally.

Кроме того, лучше попытаться использовать следующую конструкцию:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Поскольку оператор using автоматически завернут в try/finally, и поток будет автоматически закрыт. (Вам нужно будет поместить try/catch вокруг инструкции using, если вы хотите действительно поймать исключение).

Ответ 5

В то время как следующие 2 кодовых блока эквивалентны, они не равны.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  • "наконец" - это код, предназначенный для выявления намерений. Вы объявляете компилятору и другим программистам, что этот код должен работать независимо от того, что.
  • Если у вас есть несколько блоков catch, и у вас есть код очистки, вам нужно наконец. Без наконец, вы будете дублировать код очистки в каждом блоке catch. (Принцип DRY)

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

Ответ 6

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

Кроме того, с точки зрения читаемости, когда я вижу блок try, я предполагаю, что будет соответствующий оператор catch. Если вы используете "try", чтобы обеспечить выделение ресурсов в блоке "finally", вы можете рассмотреть "using" statement вместо:

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

Вы можете использовать оператор 'using' с любым объектом, который реализует IDisposable. Метод objectpose() автоматически вызывается в конце блока.

Ответ 7

Блок try..find все равно будет вызывать любые возникающие исключения. Все finally это гарантирует, что код очистки будет запущен до того, как будет создано исключение.

Try..catch с пустым уловом полностью уничтожит любое исключение и скроет тот факт, что это произошло. Читатель будет закрыт, но не знает, произошло ли правильное. Что, если вы намерены написать я в файл? В этом случае вы не перейдете к этой части кода, а myfile.txt будет пустым. Соответствуют ли все методы управления нисходящим потоком? Когда вы увидите пустой файл, сможете ли вы правильно догадаться, что он пуст, потому что выбрано исключение? Лучше бросить исключение и дать понять, что вы делаете что-то неправильно.

Другая причина заключается в том, что try..catch, сделанный как это, совершенно неверно. Что вы говорите, делая это, "независимо от того, что происходит, я могу справиться с этим". Что насчет StackOverflowException, вы можете очистить после этого? Что насчет OutOfMemoryException? В общем, вы должны обрабатывать только те исключения, которые вы ожидаете, и знать, как обращаться.

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

Неплохая практика заключается в том, чтобы добавить предложение catch только для восстановления исключения.

Ответ 12

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

Ответ 13

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

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

Dim str As Stream = GetStream() Если (str.CanRead) Тогда 'код для чтения End If

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

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

Ответ 14

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

Ответ 15

Используйте Try..Catch..Finally, если ваш метод знает, как обрабатывать исключение локально. Исключение происходит в Try, Handled in Catch и после этого очистка выполняется в завершении.

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

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

В рамках Try..Finally обеспечивается, чтобы локальная очистка выполнялась до распространения исключения до вызывающих методов.

Ответ 16

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

Ответ 17

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

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

Ответ 18

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

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

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

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

Кстати, Stream и StreamReader реализуют IDisposable и могут быть завернуты в блок "using". Блоки "Использование" - это семантический эквивалент try/finally (нет "catch" ), поэтому ваш пример может быть более кратко выражен как:

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

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

Ответ 19

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

Ответ 20

Если вы прочитаете С# для программистов, вы поймете, что блок finally был разработан для оптимизации приложения и предотвращения утечки памяти.

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

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

catch отличался от finally в том смысле, что catch был дизайном, чтобы дать вам способ обрабатывать или управлять ошибкой. Думайте об этом как о человеке, который говорит вам: "Эй, я поймал некоторых плохих парней, что вы хотите, чтобы я сделал с ними?" в то время как finally был разработан для обеспечения правильной установки ваших ресурсов. Подумайте о ком-то, что, если есть какие-то плохие парни, он будет уверен, что ваша собственность по-прежнему в безопасности.

И вы должны позволить этим двум работать вместе навсегда.

например:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}