Бросить; говорят, что это не reset трассировка стека, но это происходит в определенных обстоятельствах

Возможный дубликат:
неверный stacktrace by rethrow

Общепризнано, что в .NET throw; не reset выполняется трассировка стека, а throw ex;.

Однако в этой простой программе я получаю разные номера строк:

void Main()
{
    try
    {
        try
        {
            Wrapper(); // line 13
        }
        catch(Exception e)
        {
            Console.WriteLine(e.ToString());
            throw; // line 18
        }
    }
    catch(Exception e)
    {
          Console.WriteLine(e.ToString());
    }
}

public void Wrapper()
{
    Throw(); // line 28
}

public void Throw()
{
    var x = (string)(object)1; // line 33
}

Вывод:

System.InvalidCastException: Невозможно наложить объект типа "System.Int32" на тип "System.String".    в ConsoleApplication2.Program.Main(String [] args) в C:\long-path\Program.cs: строка 13

System.InvalidCastException: Невозможно наложить объект типа "System.Int32" на тип "System.String".    в ConsoleApplication2.Program.Main(String [] args) в C:\long-path\Program.cs: строка 18

Примечание. Первая трассировка стека содержит строку 13, вторая содержит строку 18. Кроме того, ни одна строка 13, ни строка 18 не являются строками, в которых фактически происходит аккомпанемент.

Теперь мой вопрос: в каких обстоятельствах throw; меняет трассировку стека и в каких обстоятельствах она не меняет трассировку стека?

Обратите внимание, что это уже было замечено, но не ответил в целом.


UPDATE:
Я запустил код выше в режиме отладки, и он дает следующее:

System.InvalidCastException: Невозможно наложить объект типа "System.Int32" на тип "System.String".    в ConsoleApplication2.Program.Throw() в C:\long-path\Program.cs: строка 33    в ConsoleApplication2.Program.Wrapper() в C:\long-path\Program.cs: строка 28    в ConsoleApplication2.Program.Main(String [] args) в C:\long-path\Program.cs: строка 13

System.InvalidCastException: Невозможно наложить объект типа "System.Int32" на тип "System.String".    в ConsoleApplication2.Program.Throw() в C:\long-path\Program.cs: строка 33    в ConsoleApplication2.Program.Wrapper() в C:\long-path\Program.cs: строка 28    в ConsoleApplication2.Program.Main(String [] args) в C:\long-path\Program.cs: строка 18

Обратите внимание: последний номер строки все еще изменяется

Ответ 1

Поскольку Дарин уже указывает, что уменьшенная трассировка стека обусловлена ​​методом inline. Однако есть и точка ссылки на строку, доступная в трассировке стека, не равная.

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

Я говорил об этом и приводил полные примеры по различным способам повторного исключения исключений в следующем сообщении в блоге:

.NET Исключения - выброс ex - это зло, но бросок не является невиновным


Ваш комментарий побудил меня к быстрому исследованию, но лучшее, что я смог найти, это этот комментарий Джонатана де Холлекса в блоге о catch and rethrow:

Он также изменяет номер строки в stacktrace в методе, который rethrows (поскольку rethrow становится сайтом throw в этом методе).

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

Итак, несмотря на то, что stacktrace сохраняется при использовании throw вместо throw e, исходный сайт броска будет потерян, если вы не обернете и не выбросите новое исключение.

<суб > Другие вещи, чтобы попробовать; поскольку SO не разрешает прямые сообщения, а выше комментарий был сделан Peli, вы можете попытаться пометить этот вопрос pex, чтобы привлечь его внимание и привлечь его к следить за этим комментарием.:) Суб >

Ответ 2

Причина этого происходит из-за того, что метод работает в режиме Release. Если вы не хотите, чтобы методы Wrapper и Throw были встроены в режим Release, вы можете украсить их атрибутом [MethodImpl]:

[MethodImpl(MethodImplOptions.NoInlining)]
public void Wrapper()
{
    Throw();
}

[MethodImpl(MethodImplOptions.NoInlining)]
public void Throw()
{
    var x = (string)(object)1;
}