Почему попытка {...} наконец {...} хороша; попробуй {...} поймать {} плохо? - 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 ]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. Наконец, код, раскрывающий намерения. Вы заявляете компилятору и другим программистам, что этот код должен выполняться независимо от того, что.
  2. если у вас есть несколько блоков catch и у вас есть код очистки, вам нужно наконец. Без, наконец, вы бы дублировали код очистки в каждом блоке catch. (Принцип СУХОЙ)

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

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

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

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

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

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

3 голосов
/ 20 сентября 2016

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

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

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

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

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

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

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

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

2 голосов
/ 29 апреля 2016

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

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

Например, когда вы открываете соединение с файлом или базой данных, ваш компьютер будет выделять память для обслуживания этой транзакции, и эта память будет сохраняться только после выполнения команды disposed или 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();
}
2 голосов
/ 27 сентября 2012

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

...