c# – 在switch语句中抛出异常的堆栈跟踪中的行号错误

如果在switch语句中抛出异常,我注意到异常堆栈跟踪中行号的奇怪行为。

这里是一个例子(格式化当然是因为行号):

using System;
class Program {
    static void Main(string[] args) {
        for (int i = 0; i < 3; i++) {
            try {
                ThrowSomeException(i);
            } catch (Exception exc) {
                Console.WriteLine(exc);
            }
        }
    }
    private static void ThrowSomeException(int arg) {
        Console.WriteLine("arg = {0}", arg);
        switch (arg) {
        case 0:
            throw new Exception("Line number = 16");
        case 1:
            throw new Exception("Line number = 18");
        default:
            throw new Exception("Line number = 20");
        }
    }
}

堆栈跟踪中报告的行是switch语句中下一个异常的行。上述程序产生此结果(请注意异常文本中的行号与堆栈跟踪中的行号之间的不匹配):

arg = 0
System.Exception: Line number = 16
   at Program.ThrowSomeException(Int32 arg) in x:\test\Program.cs:line 18
   at Program.Main(String[] args) in x:\test\Program.cs:line 6
arg = 1
System.Exception: Line number = 18
   at Program.ThrowSomeException(Int32 arg) in x:\test\Program.cs:line 20
   at Program.Main(String[] args) in x:\test\Program.cs:line 6
arg = 2
System.Exception: Line number = 20
   at Program.ThrowSomeException(Int32 arg) in x:\test\Program.cs:line 20
   at Program.Main(String[] args) in x:\test\Program.cs:line 6

为什么会发生这种情况?

注意:我在VS 2012和2013中尝试了这两个,在.NET 3.5和4.5上进行了编译,得到了相同的结果。

调试vs发布:令人惊讶的是,我只在Debug中得到这个奇怪的行为,在释放行号是正确的。

最佳答案
我已经尝试反编译程序集,并且似乎程序集在任一模式下进行一些优化。特别是第二种方法改变了:

private static void ThrowSomeException(int arg)
{
    Console.WriteLine("arg = {0}", arg);
    switch (arg)
    {
        case 0:
        {
            throw new Exception("Line number = 16");
        }
        case 1:
        {
            throw new Exception("Line number = 18");
        }
    }
    throw new Exception("Line number = 20");
}

这就是Telerik JustDecompile告诉我方法看起来像是Debug和Release模式。很可能如果您查看原始的程序集,将会进一步解释为什么存在这种差异。

我不知道如何继续这个,但我认为这是一个非常有趣的问题。我将把我的答案标记为社区Wiki,希望能够协同努力解决这个问题。

我做了更多的测试。我已经将ThrowSomeException()函数移动到一个单独的类,并将其非静态,并没有改变任何东西。然后我稍稍重写一遍,首先将异常分配给一个变量,然后分开抛出。

internal class Program
{
    private static void Main()
    {
        Test test = new Test();
        for (int i = 0; i < 3; i++)
        {
            try
            {
                test.ThrowSomeException(i);
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }
    }


}

public class Test
{
    public void ThrowSomeException(int arg)
    {

        Console.WriteLine("arg = {0}", arg);
        switch (arg)
        {
            case 0:
                {
                    Exception ex = new Exception("Line number = 36");
                    throw ex;
                }
            case 1:
                {
                    Exception ex = new Exception("Line number = 41");
                    throw ex;
                }
            default:
                {
                    Exception ex = new Exception("Line number = 46");
                    throw ex;

                }
        }

    }
}

上述代码在调试模式下具有以下输出:

arg = 0
System.Exception: Line number = 36
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 40
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14
arg = 1
System.Exception: Line number = 41
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 45
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14
arg = 2
System.Exception: Line number = 46
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 47
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14

在释放模式下,第二个异常从第46行而不是45抛出。对于.NET框架的所有版本和VS的所有版本,此行为是一致的。接下来我将尝试使用VB项目来做这件事,看看它是否有所作为。

编辑:在VS 2012中使用以下VB项目:

Module Program
Sub Main()
    Dim test As New Test()
    For i As Integer = 0 To 2
        Try
            test.ThrowSomeException(i)
        Catch exception As Exception
            Console.WriteLine(exception)
        End Try
    Next
End Sub
End Module

Public Class Test
Public Sub ThrowSomeException(arg As Integer)

    Console.WriteLine("arg = {0}", arg)
    Select Case arg
        Case 0
            If True Then
                Dim ex As New Exception("Line number = 22")
                Throw ex
            End If
        Case 1
            If True Then
                Dim ex As New Exception("Line number = 27")
                Throw ex
            End If
        Case Else
            If True Then
                Dim ex As New Exception("Line number = 32")
                Throw ex

            End If
    End Select

End Sub
End Class

问题不会发生,行号是一致的。

我也已经直接测试了输出生成的可执行文件,甚至发现甚至更糟糕的结果。这是debug exe的输出:

arg = 0
System.Exception: Line number = 36
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 40
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14
arg = 1
System.Exception: Line number = 41
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 45
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14
arg = 2
System.Exception: Line number = 46
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 47
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14

这是从释放模式exe的输出:

arg = 0
System.Exception: Line number = 36
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 41
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14
arg = 1
System.Exception: Line number = 41
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 42
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14
arg = 2
System.Exception: Line number = 46
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 37
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 14

这与使用调试器运行这一点不同。

快速测试als似乎表明异常和throw语句之间的额外的行(在这种情况下是console.writeline())也会影响结果:

改写为:

switch (arg)
    {
        case 0:
            {
                Exception ex = new Exception("Line number = 37");
                Console.WriteLine("case 0");
                throw ex;
            }
        case 1:
            {
                Exception ex = new Exception("Line number = 43");
                Console.WriteLine("case 1");
                throw ex;
            }
        default:
            {
                Exception ex = new Exception("Line number = 49");
                Console.WriteLine("case default");
                throw ex;                    
            }
    }

给出VS的Release Release:

arg = 0
case 0
System.Exception: Line number = 37
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 43
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 1
case 1
System.Exception: Line number = 43
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 49
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 2
case default
System.Exception: Line number = 49
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 51
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15

并从命令行输出:

arg = 0
case 0
System.Exception: Line number = 37
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 43
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 1
case 1
System.Exception: Line number = 43
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 45
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 2
case default
System.Exception: Line number = 49
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 39
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15

和VS的Debug输出:

arg = 0
case 0
System.Exception: Line number = 37
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 42
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 1
case 1
System.Exception: Line number = 43
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 48
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 2
case default
System.Exception: Line number = 49
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 51
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15

并从命令行输出:

arg = 0
case 0
System.Exception: Line number = 37
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 42
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 1
case 1
System.Exception: Line number = 43
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 48
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15
arg = 2
case default
System.Exception: Line number = 49
   bij linenumbertest.Test.ThrowSomeException(Int32 arg) in c:\Users\nate\Docume
nts\Visual Studio 2012\Projects\linenumbertest\Program.cs:regel 51
   bij linenumbertest.Program.Main() in c:\Users\nate\Documents\Visual Studio 20
12\Projects\linenumbertest\Program.cs:regel 15

转载注明原文:c# – 在switch语句中抛出异常的堆栈跟踪中的行号错误 - 代码日志