Почему попробуйте {…} наконец {…} хорошо;попытаться {…} поймать{} плохо?

StackOverflow https://stackoverflow.com/questions/128818

Вопрос

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

Иначе, что такого особенного в «наконец-то»?

Это было полезно?

Решение

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

Другие советы

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

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

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

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

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

Со вторым блоком исключением будет брошенный и пузырится но тот reader.Close() все еще гарантированно работает.

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

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

Также лучше попробовать использовать следующую конструкцию:

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

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

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

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 будет всегда выполняться.Это не просто синтаксический сахар от компилятора.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Dim Str As stream = getStream () if (str.canread), затем код для чтения конец потока, если

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

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

Добавлять пункт catch только для повторной генерации исключения — плохая практика.

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top