Страницы

Поиск по вопросам

четверг, 12 декабря 2019 г.

Получить реальное место возникновения ошибки

#c_sharp #net


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

Можно ли как-то получить реальное место возникновения ошибки?

Пример кода:

 static void Main(string[] args)
    {
        try
        {
            var t = 1;
            var t3 = 2;
            var t4 = t - t3;
            throw new Exception("BlaBla bla");
        }
        catch (Exception ex)
        {
            throw;
        }
    }




Тут у меня пишется, что исключение возникло не в throw new Exception("BlaBla bla");
, а на строчке с throw;
    


Ответы

Ответ 1



Делайте throw; // перебросить с оригинальным стектрейсом А не throw ex; // бросить заново и потерять стектрейс К сожалению, если исключение было брошено, поймано, и переброшено в одном и том же методе - строчка в стектрейсе поменяется даже в случае если был использован throw;. CLR использует SEH, а тот использует стекфремы для отслеживания цепочки вызовов. На один метод - один стекфрейм, так что перебрасывание исключения в рамках одного метода перетирает оригинальный stack trace. Т.е. любой метод бросания исключения - и throw;, и throw ex; - изменяют стектрейс. Просто первый сохраняет более глубокие фреймы, а второй - нет. Вопрос на английском: enSO: Incorrect stacktrace by rethrow

Ответ 2



Это можно сделать на современном C#, хотя и не так изящно, как хотелось бы. Вам нужно поместить логику до throw в фильтр исключений, и вернуть из него false для случая, когда исключение нужно пробросить. При этом код вообще не будет заходить в catch. Вместо метода static void Main(string[] args) { var t = 1; try { var t3 = 2; var t4 = t - t3; throw new Exception("BlaBla bla"); } catch (Exception ex) { Console.WriteLine($"Exception with message {ex.Message} thrown, t = {t}"); throw; } } (тут в StackTrace только строка с throw) у вас получится вот такое: static void Main(string[] args) { var t = 1; try { var t3 = 2; var t4 = t - t3; throw new Exception("BlaBla bla"); } catch (Exception ex) when (Filter(ex)) { } bool Filter(Exception ex) { Console.WriteLine($"Exception with message {ex.Message} thrown, t = {t}"); return false; } } (в StackTrace только строка с throw new Exception("BlaBla bla");). Ещё один популярный (в узких кругах) вариант — использование System.Runtime.ExceptionServices.ExceptionDispatchInfo. Вот такой код: static void Main(string[] args) { var t = 1; try { var t3 = 2; var t4 = t - t3; throw new Exception("BlaBla bla"); } catch (Exception ex) { Console.WriteLine($"Exception with message {ex.Message} thrown, t = {t}"); ExceptionDispatchInfo.Capture(ex).Throw(); } } производит StackTrace из двух частей: at Test.Program.Main(String[] args) in ...\Program.cs:line 38 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Test.Program.Main(String[] args) in ...\Program.cs:line 43 Строка 38 содержит throw new Exception("BlaBla bla");, а строка 43 — ExceptionDispatchInfo.Capture(ex).Throw();. (Я вынес переменную t, чтобы к ней можно было обращаться из блока catch ну или из локальной функции.) Ограничение метода: невозможно использовать async-метод в фильтре. При большом желании, фильтр можно и заинлайнить. Синтаксис при этом становится совсем вырвиглазным.

Ответ 3



Можно: try { ... } catch(ExceptionType ex) { throw; // а не throw ex; }

Комментариев нет:

Отправить комментарий