C# 投掷;据说不会重置堆栈跟踪,但在某些情况下会重置堆栈跟踪
可能重复:C# 投掷;据说不会重置堆栈跟踪,但在某些情况下会重置堆栈跟踪,c#,.net,C#,.net,可能重复: 人们普遍认为,在.NETthrow不重置堆栈跟踪,但抛出ex可以 然而,在这个简单的程序中,我得到了不同的行号: void Main() { try { try { Wrapper(); // line 13 } catch(Exception e) { Console.WriteLine(e.ToString());
人们普遍认为,在.NET
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”。
在C:\long path\Program.cs中的ConsoleApplication2.Program.Main(字符串[]args)处:第13行
System.InvalidCastException:无法将类型为“System.Int32”的对象强制转换为类型为“System.String”。
在C:\long path\Program.cs中的ConsoleApplication2.Program.Main(字符串[]args)处:第18行
注意:第一个堆栈跟踪包含第13行,第二个堆栈跟踪包含第18行。此外,第13行和第18行都不是演员实际发生的地方
我现在的问题是:在什么情况下抛出代码>更改堆栈跟踪,在哪种情况下不更改堆栈跟踪
请注意,这一点已经得到了回答,但总体上没有得到回答
更新:
我在调试模式下运行了上面的代码,结果如下:
System.InvalidCastException:无法将类型为“System.Int32”的对象强制转换为类型为“System.String”。
在控制台C:\long path\Program.cs中的application2.Program.Throw()处:第33行
在C:\long path\Program.cs中的ConsoleApplication2.Program.Wrapper()处:第28行
在C:\long path\Program.cs中的ConsoleApplication2.Program.Main(字符串[]args)处:第13行
System.InvalidCastException:无法将类型为“System.Int32”的对象强制转换为类型为“System.String”。
在控制台C:\long path\Program.cs中的application2.Program.Throw()处:第33行
在C:\long path\Program.cs中的ConsoleApplication2.Program.Wrapper()处:第28行
在C:\long path\Program.cs中的ConsoleApplication2.Program.Main(字符串[]args)处:第18行
请注意:最后一行号仍在更改发生这种情况的原因是在发布模式下运行时方法内联。如果您不希望包装器
和抛出
方法在发布模式下内联,可以使用[MethodImpl]
属性对它们进行修饰:
[MethodImpl(MethodImplOptions.NoInlining)]
public void Wrapper()
{
Throw();
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Throw()
{
var x = (string)(object)1;
}
正如Darin已经指出的,减少的堆栈跟踪是由于方法内联。但是,堆栈跟踪中可用的线参考点也不相等
我不知道这背后的解释,但有一种方法可以让您在重新引发异常时保留所有stacktrace信息。您需要抛出一个新异常,并将捕获的异常作为内部异常传递。使用这种方法,合并的stacktrace将包含异常的起始点以及异常被重新恢复的点
我在下面的博文中讨论了这一点,并给出了关于重新引用异常的不同方法的完整示例:
你的评论促使我快速进行研究,但我能找到的最好的答案是乔纳森·德·哈利乌在一篇关于以下内容的博客文章中的评论:
它还更改stacktrace中的行号,方法如下:
重新插入(因为重新插入成为该方法的插入位置)
这一点可以进一步阐述,但它指出了这样一个事实,即可能会在每个方法中跟踪用于获取行信息的抛出站点,并且重新抛出会导致其被覆盖
因此,即使在使用throw
而不是throw e
时保留stacktrace,原始的throw站点也将丢失,除非您包装并抛出新的异常
其他需要尝试的事情;由于SO不允许直接留言,且上述评论是由您提出的,您可以尝试在该问题上添加pex
,以引起他的注意,并让他跟进该评论。:)
好的,这就解释了为什么第一个锁扣中的线号不是铸件的线号。它仍然不能解释为什么抛出代码>更改堆栈跟踪中的行号。我正要说一些关于这些行的话,因此只需补充:在调试模式下运行代码会生成完整的调用堆栈,这两个调用堆栈都源自“Throw”方法。@DanielHilgarth您看到的行号是重新抛出异常的行。在一个发布版本中,这就是您所能得到的,因为其余部分已经内联。如果你想知道异常的来源,你需要完整的堆栈。如果最后一行号没有改变,你就无法判断异常是否被重新抛出。抛出e之间的差异代码>和抛出
是第一个将在重新抛出之前清除堆栈,而后者将保留堆栈。在这两种情况下,重新抛出都会添加到堆栈中,就像它应该添加的一样。@BrianRasmussen:throw代码>仍然会丢失重要信息。如果try
中的内容不仅仅是对一个方法的一次调用,而是像这样:int.Parse(x1);int.Parse(x2)代码>(当然在单独的行上)。在抛出
之后,您将不知道这两个异常中的哪一个抛出了异常。谢谢。我知道包装它将保留完整的堆栈跟踪。我问这个问题是为了给将来的帖子提供一个参考,在这些帖子中,人们会说只使用throw代码>“因为它保留堆栈跟踪”,但事实上它并不总是这样做。我用interwebs中的进一步信息更新了这个问题,您可能已经看到了,但可能对参考有用。但是,它仍然没有完全解释这个主题。如果异常中详细的行号与您提供的代码片段相关,这会有所帮助。。。