一位同事在他的VB.net解决方案中发现了有线调试器行为。我承认这将是一个学术问题,因为这只会影响调试时突出显示的语句序列,而不会影响代码的整体行为。所以对于那些好奇的人来说:
我们将其删除到以下最低控制台应用程序:
Private Sub PlayWithExceptions
Dim a = 2
Try
throw new Exception("1")
Catch ex As Exception
If a = 2 Then
Dim x = New XElement("Dummy")
Else
throw
End If
End Try
End Sub
Sub Main()
Try
PlayWithExceptions()
Catch ex As Exception
End Try
End Sub
很明显,调试器抛出Exception(“1”),调试器跳转到PlayWithExceptions方法的catch子句。在那里,因为“a”总是2,调试器跳转到一些虚拟代码(New XElement ...),从那里到“End If”,最后返回到Else-leaf到throw语句 。我承认Visual Studio不会重新抛出异常,但它看起来很奇怪。
将条件“如果a = 2”更改为“If True”会消除此行为。
重构为条件捕获也消除了这种行为。
Private Sub PlayWithExceptions
Dim a = 2
Try
throw new Exception("1")
Catch ex As Exception When a = 2
Dim x = New XElement("Dummy")
Catch ex As Exception
throw
End Try
End sub
将这几行转换为C#也不会显示此行为。
private static void PlayWithExceptions()
{
var a = 2;
try
{
throw new Exception("1");
}
catch (Exception)
{
if (a == 2)
{
var x = new XElement("Dummy");
}
else
{
throw;
}
}
}
static void Main(string[] args)
{
try
{
PlayWithExceptions();
}
catch (Exception ex)
{
}
}
我们尝试了.Net3.5和.Net4.6以及目标AnyCPU和x86,对上述VB代码没有任何影响。代码是使用默认的Debug设置执行的,没有进一步的优化。我们使用了VS2015 Update 3.
有没有人知道为什么Visual Studio假装在VB中重新抛出异常(但没有真正重新抛出它)?调试时看起来很混乱......
答案 0 :(得分:5)
它与隐藏代码有关,它为VB.Net的Err
对象设置/取消设置错误信息 - 在源中没有真正的“位置”。
在IL中,清除错误的代码位于rethrow
调用之后,因此它是即将调用它时可以显示的最接近的源代码行。我无法回答的是,为什么它在调用它之前停止它应该只是在(可见的)源代码行之间踩踏。
但是如果在调试器位于Err
行时检查Throw
对象,您将看到它有一个当前的异常对象。而在此之后的步骤中,当前异常已被清除。请参阅下面的IL_0035
了解调试器暂停的位置:
.method private static void PlayWithExceptions() cil managed
{
// Code size 62 (0x3e)
.maxstack 2
.locals init ([0] int32 a,
[1] class [mscorlib]System.Exception ex,
[2] bool V_2,
[3] class [System.Xml.Linq]System.Xml.Linq.XElement x)
IL_0000: nop
IL_0001: ldc.i4.2
IL_0002: stloc.0
.try
{
IL_0003: nop
IL_0004: ldstr "1"
IL_0009: newobj instance void [mscorlib]System.Exception::.ctor(string)
IL_000e: throw
} // end .try
catch [mscorlib]System.Exception
{
IL_000f: dup
IL_0010: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception)
IL_0015: stloc.1
IL_0016: nop
IL_0017: ldloc.0
IL_0018: ldc.i4.2
IL_0019: ceq
IL_001b: stloc.2
IL_001c: ldloc.2
IL_001d: brfalse.s IL_0032
IL_001f: ldstr "Dummy"
IL_0024: call class [System.Xml.Linq]System.Xml.Linq.XName [System.Xml.Linq]System.Xml.Linq.XName::op_Implicit(string)
IL_0029: newobj instance void [System.Xml.Linq]System.Xml.Linq.XElement::.ctor(class [System.Xml.Linq]System.Xml.Linq.XName)
IL_002e: stloc.3
IL_002f: nop
IL_0030: br.s IL_0035
IL_0032: nop
IL_0033: rethrow
//Debugger is pausing at IL_0035 when the highlight is on Throw
IL_0035: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
IL_003a: leave.s IL_003c
} // end handler
IL_003c: nop
IL_003d: ret
} // end of method Module1::PlayWithExceptions
对于If True
变体,它甚至不再包含Throw
代码,因此它显然永远不会相信它将要执行它。对于具有异常过滤器的变体,每个 Catch
子句独立管理其SetProjectError
/ ClearProjectError
个调用,因此在调用Throw
之间不会产生混淆而那个叫New XElement
的那个。