none
Когда нужно использовать конструкцию try \ catch RRS feed

  • Вопрос

  • Здравствуйте, никогда не использовал отлов исключений...
    У моего коллеги мания - везде использует rконструкцию try \ catch: при создании объектов, при работе с базой данных...

    У нас разгорелась битва словесная...Я в свою защиту только могу ответить: try \ catch достаточно тяжёлая операция, чтобы её не использовать .
    А что бы посоветовали - использовать её и в каких случаях?
    28 июля 2012 г. 10:52

Ответы

  • Ваш основной аргумент не применим конкретно в этом споре :)

    try/catch - не тяжелая операция. Это просто очень распространненный миф. Более того, это не "операция" в том смысле, что она не ест процессор, и вообще не поглощаяет ресурсов. 

    Вот древняя, но правдивая статья на MSDN: Performance Tips and Tricks in .NET Applications:

    Bear in mind that this has nothing to do with try/catch blocks: you only incur the cost when the actual exception is thrown. You can use as many try/catch blocks as you want.

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

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

    Исключения стоит ловить только при:

    • необходимости логгирования (с перебрасыванием оригинального исключения через throw;)
    • возможности добавить к уже брошенному исключению какой-то информации (с перебрасыванием нового исключения, и выставлением у него inner exception)
    • необходимости спрятать от вызывающего кода сам факт ошибки (при возможности полностью корректно продолжить работу, например, повтором упавшего запроса).
    • необходимости перебросить исключение в другом месте (например другом потоке).

    Для работы с базой данных/файлами/другими IDisposable вещами лучше использовать конструкцию using. Это тот же try/catch/finally, с корректно написанным блоком finally.

    • Помечено в качестве ответа ansi_str 30 июля 2012 г. 13:33
    30 июля 2012 г. 8:19
    Модератор
  • >> открытие и закрытие 10000 тысячь секций будет медленнее, чем взять весь цикл в один try/catch.

    Абсолютно одинаково в обоих случаях. Просто потому, что на самом деле никакого открытия / закрытия секции не происходит. try компилируется в nop/jmp через catch (в дебаге). И в jmp - в релизе. Если catch стоит в конце for - то try/catch вообще никак не будет видет в результирующем машинном коде (легко проверить в студии, под отладкой).

    В первом приближении работает примерно так: CLR регистрирует один обработчик SEH, сразу на всю программу. В момент бросания исключения (SEH-исключения) его ловит CLR, лезет в IL, проверяет, находится ли вызываемый код внутри try/catch, и вызывает catch, отдавая ему управляемое исключение. Если исключения не было - то и затрат на try/catch не было. Если исключение было брошено - то затраты будут в любом случае, вне зависимости от того, поймано оно пользовательским try/catch, или системным.

    • Помечено в качестве ответа ansi_str 30 июля 2012 г. 13:33
    30 июля 2012 г. 8:41
    Модератор

Все ответы

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

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

    • Изменено INFEL8 28 июля 2012 г. 12:00
    28 июля 2012 г. 11:59
  • По сути бывают места, где без данной конструкции не обойтись, а бывают места, где она совсем не к месту.

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

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


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    28 июля 2012 г. 16:08
    Отвечающий
  • Одна из самых распространённых ошибок, это слишком частое и неуместное использование блоков try/catch. "Я в свою защиту только могу ответить: try \ catch достаточно тяжёлая операция, чтобы её не использовать " - не только, основная причина - это невозможность предвидеть и обработать все исключения. "А что бы посоветовали - использовать её и в каких случаях?" - ловить только те исключения которые мы знаем и можем обработать. Т.е. не применять подобные конструкции:

    try
    {
     //Код
    }
    catch(Exception e)
    {
     //Код
    }

    А что значит знаем? Например, исключение произошедшеее в процессе открытия файла, по причине его отсутсвия. Мы можем его перехватить и дать пользователю возможность выбрать другой не останавливая приложение. А ислючение произошедшее в при попытке чтения объекта из повреждённой памяти мы не в состоянии правильно понять и обработать. Те исключения которые мы не знаем пустить вверх по стеку вызовов, которые могут привести к останову приложения, но не вкоем случае не проглатывать их. Лучше так, чем получить такую катастрофу, как - "неверно работающие объекты".

    28 июля 2012 г. 19:01
    Модератор
  • Ну и еще одна рекомендация, не используйте try/catch внутри юлинных циклов, т.к. если у вас цикл на 10000 записей, то открытие и закрытие 10000 тысячь секций будет медленнее, чем взять весь цикл в один try/catch.

    Т.е. не пишите вот так:

    foreach (var item in bigCollection)
    {
        try
        {
            // операция над элементом которая может привести к ошибке
        }
        catch { ... }
    }

    Пишите вот так:

    try
    {
        foreach (var item in bigCollection)
        {
            // операция над элементом которая может привести к ошибке
        }
    }
    catch { ... }
    

    29 июля 2012 г. 6:06
    Отвечающий
  • Ну и еще одна рекомендация, не используйте try/catch внутри юлинных циклов, т.к. если у вас цикл на 10000 записей, то открытие и закрытие 10000 тысячь секций будет медленнее, чем взять весь цикл в один try/catch.

    Т.е. не пишите вот так:

    foreach (var item in bigCollection)
    {
        try
        {
            // операция над элементом которая может привести к ошибке
        }
        catch { ... }
    }

    Пишите вот так:

    try
    {
        foreach (var item in bigCollection)
        {
            // операция над элементом которая может привести к ошибке
        }
    }
    catch { ... }

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

    foreach (var item in bigCollection)
    {
        try
        {
            // операция над элементом которая может привести к ошибке
        }
        catch { ... }
    }



    29 июля 2012 г. 15:29
  • В этом случае м могу поспорить 
    Если один элемент битый, чтобы итерация не прекратилась, придётся писать...
    Таких случаев все же лучше избегать, так ак этот код будет очень долго работать, но все зависит от случая. Как я и сказал выше бывают случаи, когда без try/catch не обойтись, даже внутри цикла.

    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    29 июля 2012 г. 15:32
    Отвечающий
  • Ваш основной аргумент не применим конкретно в этом споре :)

    try/catch - не тяжелая операция. Это просто очень распространненный миф. Более того, это не "операция" в том смысле, что она не ест процессор, и вообще не поглощаяет ресурсов. 

    Вот древняя, но правдивая статья на MSDN: Performance Tips and Tricks in .NET Applications:

    Bear in mind that this has nothing to do with try/catch blocks: you only incur the cost when the actual exception is thrown. You can use as many try/catch blocks as you want.

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

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

    Исключения стоит ловить только при:

    • необходимости логгирования (с перебрасыванием оригинального исключения через throw;)
    • возможности добавить к уже брошенному исключению какой-то информации (с перебрасыванием нового исключения, и выставлением у него inner exception)
    • необходимости спрятать от вызывающего кода сам факт ошибки (при возможности полностью корректно продолжить работу, например, повтором упавшего запроса).
    • необходимости перебросить исключение в другом месте (например другом потоке).

    Для работы с базой данных/файлами/другими IDisposable вещами лучше использовать конструкцию using. Это тот же try/catch/finally, с корректно написанным блоком finally.

    • Помечено в качестве ответа ansi_str 30 июля 2012 г. 13:33
    30 июля 2012 г. 8:19
    Модератор
  • >> открытие и закрытие 10000 тысячь секций будет медленнее, чем взять весь цикл в один try/catch.

    Абсолютно одинаково в обоих случаях. Просто потому, что на самом деле никакого открытия / закрытия секции не происходит. try компилируется в nop/jmp через catch (в дебаге). И в jmp - в релизе. Если catch стоит в конце for - то try/catch вообще никак не будет видет в результирующем машинном коде (легко проверить в студии, под отладкой).

    В первом приближении работает примерно так: CLR регистрирует один обработчик SEH, сразу на всю программу. В момент бросания исключения (SEH-исключения) его ловит CLR, лезет в IL, проверяет, находится ли вызываемый код внутри try/catch, и вызывает catch, отдавая ему управляемое исключение. Если исключения не было - то и затрат на try/catch не было. Если исключение было брошено - то затраты будут в любом случае, вне зависимости от того, поймано оно пользовательским try/catch, или системным.

    • Помечено в качестве ответа ansi_str 30 июля 2012 г. 13:33
    30 июля 2012 г. 8:41
    Модератор