Что может привести к сбросу сброса callstack (я использую "throw", а не "throw ex")
Я всегда думал, что разница между "throw" и "throw ex" заключается в том, что throw не сбрасывает трассировку стека исключения.
К сожалению, это не то поведение, которое я испытываю; Вот простой пример, воспроизводящий мою проблему:
using System;
using System.Text;
namespace testthrow2
{
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("line 14");
}
catch (Exception)
{
throw; // line 18
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
}
}
Я ожидаю, что этот код напечатает стек вызовов, начиная со строки 14; однако стек вызовов начинается со строки 18. Конечно, в образце это не имеет большого значения, но в моем реальном приложении потеря первоначальной информации об ошибке довольно болезненна.
Я что-то упускаю из виду? Есть ли другой способ достичь того, что я хочу (т.е. перебрасывать исключение без потери информации стека?)
Я использую.net 3.5
3 ответа
Вы должны прочитать эту статью:
Короче, throw
обычно сохраняет трассировку стека исходного сгенерированного исключения, но только если исключение не произошло в текущем кадре стека (т. е. метод).
Есть метод PreserveStackTrace
(показано в этой статье блога), который вы используете, который сохраняет исходную трассировку стека, как это:
try
{
}
catch (Exception ex)
{
PreserveStackTrace(ex);
throw;
}
Но мое обычное решение состоит в том, чтобы либо просто не перехватывать и не перебрасывать исключения, подобные этому (если это абсолютно не нужно), либо просто всегда генерировать новые исключения, используя InnerException
Свойство для распространения исходного исключения:
try
{
}
catch (Exception ex)
{
throw new Exception("Error doing foo", ex);
}
Проблема в том, что Windows сбрасывает начальную точку стека. CLR ведет себя как ожидалось - это всего лишь ограничение поддержки обработки исключений операционной системой хоста. Проблема в том, что на вызов метода может быть только один кадр стека.
Вы можете выделить свои процедуры обработки исключений в отдельный "вспомогательный" метод, который обойдет ограничения, накладываемые Windows SEH, но я не думаю, что это обязательно хорошая идея.
Правильный способ перебросить исключение без потери информации стека - это создать новое исключение и включить исходное, перехваченное исключение в качестве внутреннего исключения.
Трудно представить очень много случаев, когда вам действительно нужно это сделать. Если вы не обрабатываете исключение, а просто ловите его, чтобы выбросить, вы, вероятно, не должны его ловить в первую очередь.
Обычное повторное преобразование сохраняет все в трассировке стека, за исключением того, что если текущий метод находится в трассировке стека, номер строки будет перезаписан. Это раздражающее поведение. В C#, если нужно что-то сделать в исключительном случае, но не волнует, что это за исключение, можно использовать шаблон:
Boolean ok = False; пытаться { сделай что-нибудь(); хорошо = правда; } в конце концов { if (!ok) // Возникла исключительная ситуация! handle_exception(); }
Есть ряд, где этот шаблон очень полезен; наиболее распространенной будет функция, которая должна возвращать новый IDisposable. Если функция не вернется, одноразовый предмет должен быть очищен. Обратите внимание, что любые операторы "return" в вышеуказанном блоке "try" должны установить ok в значение true.
В vb.net можно использовать шаблон, который функционально немного приятнее, хотя в коде одно место немного странное с шаблоном:
Dim PendingException As Exception = Nothing; Пытаться Сделай что-нибудь PendingException = Nothing 'См. Примечание Поймать Ex как исключение, когда CopyFirstParameterToSecondAndReturnFalse(Ex, PendingException) Throw 'никогда не выполнится, так как выше вернет false в заключение Если PendingException ничего не значит.. Обрабатывать исключение EndIf Конец попробовать
Функция с длинным именем должна быть реализована очевидным образом. Этот шаблон имеет то преимущество, что делает исключение доступным для кода. Хотя это часто не требуется в ситуациях с обработкой, но не перехвата, есть одна ситуация, в которой это может быть неоценимо: если процедура очистки выдает исключение. Обычно, если процедура очистки выдает исключение, любое ожидающее исключение будет потеряно. Однако с помощью приведенного выше шаблона можно обернуть ожидающее исключение в исключение очистки.
Одна интересная заметка с приведенным выше кодом: исключение может достигнуть "Catch When", но оператор Try может завершиться нормально. На самом деле не совсем ясно, что должно происходить в таких обстоятельствах, но ясно одно: утверждение "Окончание" не должно действовать так, как если бы исключение находилось на рассмотрении. Очистка PendingException сделает так, что если исключение исчезнет, код будет вести себя так, как будто его никогда не было. Альтернативой может быть обернуть и перебросить исключение, которое, как известно, произошло, поскольку эта ситуация почти наверняка указывает на что-то не так с внутренним кодом обработки исключений.