I have noticed a strange behavior with the line number in an exception's stack trace if the exception is thrown inside a switch
statement.
Here is an example (the formatting matters of course because of line numbers):
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");
}
}
}
The line reported in the stack trace is the line of the next exception in the switch statement. The above program produces this result (note the mismatch between line number in the exception text and the line number in the stack trace):
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
Why is this happening?
Note: I tried this both with VS 2012 and 2013, compiling both on .NET 3.5 and 4.5 and got the same results.
Debug vs Release: surprisingly enough, I'm getting this strange behavior in Debug only, in Release the line numbers are correct.
I have tried decompiling the assembly, and it seems like the assembly does some optimization in either mode. In particular the second method gets changed:
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");
}
This is what Telerik JustDecompile tells me the method looks like, both in Debug and Release mode. It is very possible that if you view the raw assembly, there will be a further explanation as to why this discrepancy exists.
I do not know how to continue this, but I think this is a very interesting problem. I'm going to mark my answer as Community Wiki, in the hopes that a collaborative effort can resolve this.
I have done some more testing. I've moved the ThrowSomeException() function to a separate class and made it non-static, and that didn't change anything. I then rewrote it slightly to first assign the exception to a variable and then throw it separately.
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;
}
}
}
}
The above code has the following output in Debug mode:
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
In Release mode, the second exception is thrown from line 46 instead of 45. This behavior is consistent for all versions of the .NET framework and all versions of VS. I'm next going to try doing this using a VB project to see if it makes a difference.
Edit: using the following VB project in VS 2012:
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
The problem does not occur and the line numbers are consistent.
I have also tested the executables generated by the output directly, and found even weirder results. This is the output of the 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
And this is the output from the release mode 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
This is not the same result as using the debugger to run this.
Quick testing als seems to indicate that extra lines between the exception and throw statement (in this case a console.writeline() also affect the result:
rewritten switch to this:
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;
}
}
gives this Release output from VS:
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
and this output from command line:
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
and this Debug output from VS:
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
And this output from the command line:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With