【问题标题】:Weird debugger behavior in VB.netVB.net 中奇怪的调试器行为
【发布时间】:2017-02-27 17:57:12
【问题描述】:

一位同事在他的 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 不会重新抛出异常,但它看起来很奇怪。

将条件“If 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 代码没有任何影响。代码使用默认调试设置执行,没有进一步优化。我们使用了 VS2015 Update 3。

有谁知道为什么 Visual Studio 会假装在 VB 中重新抛出异常(但没有真正重新抛出它)?调试时看起来很混乱……

【问题讨论】:

  • 我想这只能由 vb.net/vs 调试器团队的人来回答

标签: c# vb.net debugging exception


【解决方案1】:

这与设置/取消设置 VB.Net 的 Err 对象的错误信息的隐藏代码有关 - 该对象在源中没有真正的“位置”。

在 IL 中,清除错误的代码紧跟在 rethrow 调用之后,因此这是它在调用它时可以显示的最接近的源代码行。我无法回答的是为什么它应该在(可见)源代码行之间跨步时在调用它之前停止。

但是,如果您在调试器位于 Throw 行时检查 Err 对象,您会看到它有一个当前异常对象。而在那之后的步骤中,当前的异常已被清除。请参阅下面的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 代码,因此它显然永远无法相信它即将执行它。对于带有异常过滤器的变体,each Catch 子句独立管理其 SetProjectError/ClearProjectError 调用,因此在调用 Throw 和调用 @987654333 之间没有混淆@。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-21
    • 1970-01-01
    相关资源
    最近更新 更多