Сохранение исходной трассировки стека / номеров строк в исключениях .NET
-
22-09-2019 - |
Вопрос
Понимание разницы между бросить бывшего и бросок, почему исходная трассировка стека сохранена в этом примере:
static void Main(string[] args)
{
try
{
LongFaultyMethod();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
static void LongFaultyMethod()
{
try
{
int x = 20;
SomethingThatThrowsException(x);
}
catch (Exception)
{
throw;
}
}
static void SomethingThatThrowsException(int x)
{
int y = x / (x - x);
}
Но не в этом случае:
static void Main(string[] args)
{
try
{
LongFaultyMethod();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
static void LongFaultyMethod()
{
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception)
{
throw;
}
}
Второй сценарий выдает тот же результат, что и бросить бывшего стал бы?
В обоих случаях ожидается увидеть номер строки, в которой инициализируется y.
Решение
Я не уверен, относится ли это ограничение к языку C #, CLI или к их реализации Microsoft, но ваш второй пример - это случай, когда явный вызов Exception.InternalPreserveStackTrace
требуется, как задокументировано в следующем посте.Поскольку этот метод является internal
, как правило, это должно быть вызвано через отражение.Проблемы с производительностью, связанные с этим, могут быть почти полностью устранены путем создания Action<Exception>
для вызова, как показано в конце этого ответа.
Ссылка: Повторное перемещение исключений и сохранение полной трассировки стека вызовов
Редактировать: После повторного изучения ECMA-335, Раздел I §12.4.2 (обработка исключений) и Раздел III § 4.24 (повторный ввод), я теперь считаю, что поведение, которое вы видите, является семантической ошибкой в среде CLR (реализация CLI от Microsoft).Единственная конкретная ссылка на поведение - это "A rethrow
не изменяет трассировку стека в объекте ". В описанном здесь случае повторное перемещение фактически изменяет трассировку стека, делая PreserveStackTrace
найдите обходной путь для устранения ошибки know CLR.
static void LongFaultyMethod()
{
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception ex)
{
PreserveStackTrace(ex); // <-- add this line
throw;
}
}
PreserveStackTrace
вот оптимизация той, что взята из этой записи в блоге:
private static readonly Action<Exception> _internalPreserveStackTrace =
(Action<Exception>)Delegate.CreateDelegate(
typeof(Action<Exception>),
typeof(Exception).GetMethod(
"InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic));
public static void PreserveStackTrace(Exception e)
{
_internalPreserveStackTrace(e);
}
Другие советы
Потому что во втором примере вы повторно создаете исключение из того же метода.Во-первых, его выбросили из другого метода, вот почему.В области одного метода трассировка стека может быть только одной.
Поступайте следующим образом, лучший способ - это всегда переносить исключение внутрь нового исключения, чтобы вы видели глубину исключения.
"Если повторный ввод был выполнен в тем же методом (трассировка стека исключений содержит только одну информацию о номере строки для каждого метода вы никогда не увидите трассировку стека это в методе A, в строке под номером 2 было сгенерировано исключение, а затем тем же самым Методом A оно было повторно скопировано из строки номер 17, оно будет содержать только последнюю номер строки, из которой было создано исключение повторно скопировано "
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception ex)
{
// do something here.. like log or something
throw new Exception("Internal Exception", ex);
}
Я удивлен , что так много комментариев не читают мой комментарий !!Я написал в комментарии, что вам, вероятно, следует безопасно зарегистрировать это, существуют различные причины, если код верхнего уровня поглощает исключение, и вы не знаете, какое и где было сгенерировано исключение, ведение журнала поможет вам пересечь исключение !!!
Если вам не нужно регистрироваться, то не перехватывайте исключение.