2016-10-18 4 views
4

Ein Kollege fand in seiner VB.net-Lösung ein verdrahtetes Debugger-Verhalten. Ich gebe zu, dass dies eher eine akademische Frage sein wird, da dies nur die Reihenfolge der hervorgehobenen Anweisungen beim Debuggen beeinflusst und nicht das Gesamtverhalten des Codes. Also für alle neugierig da draußen:Seltsames Debugger-Verhalten in VB.net

Wir diese Mindestkonsolenanwendung auf die folgende abgespeckte:

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 

Wie sich der Debugger throws Exception („1“) und dem Debugger springt in die catch-Klausel von PlayWithExceptions Methode. Da "a" immer 2 ist, springt der Debugger zu einem Dummy-Code (New XElement ...), von dort zum "End If" und schließlich zurück in das Else-Blatt auf die throw-Anweisung. Ich gestehe, dass Visual Studio die Ausnahme nicht erneut auslöst, aber trotzdem sieht es sehr seltsam aus.

Ändern der Bedingung "Wenn a = 2" in "If True" beseitigt dieses Verhalten.

Das Refactoring auf bedingte Fänge eliminiert dieses Verhalten ebenfalls.

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 

Das Übersetzen dieser wenigen Zeilen in C# zeigt dieses Verhalten ebenfalls nicht.

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) 
    { 
    } 
} 

Wir versuchen NET3.5 und .Net4.6 sowie die Ziele AnyCPU und x86 ohne Auswirkung auf den oben VB-Code. Der Code wurde mit den Standard-Debug-Einstellungen und ohne weitere Optimierungen ausgeführt. Wir haben VS2015 Update 3 verwendet.

Hat jemand eine Idee, warum Visual Studio vorgibt, die Ausnahme in VB neu zu werfen (aber ohne es wirklich neu zu werfen)? Es sieht beim Debuggen verwirrend aus ...

+0

Ich denke, das kann nur jemand vom vb.net/vs Debugger-Team beantworten –

Antwort

5

Es bezieht sich auf versteckten Code, der Fehlerinformationen für VB.Net Err-Objekt setzt/nicht setzt - die keinen echten "Speicherort" in der Quelle hat.

In der IL befindet sich der Code zum Löschen des Fehlers unmittelbar nach dem Aufruf rethrow, und das ist also die nächstgelegene Quellzeile, die angezeigt werden kann, wenn sie aufgerufen wird. Was ich nicht beantworten kann, ist, warum es aufhört, bevor es aufgerufen wird, wenn es nur zwischen (sichtbare) Quelllinien treten sollte.

Aber wenn Sie das Objekt Err untersuchen, wenn der Debugger in der Zeile Throw ist, sehen Sie, dass es ein aktuelles Ausnahmeobjekt hat. Während auf dem Schritt danach die aktuelle Ausnahme gelöscht wurde. Siehe IL_0035 unten, wo der Debugger anhalten wird:

.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 

Für die If True Variante, ist es nicht einmal gehört den Throw Code mehr und so kann es natürlich nie glauben, dass es über ist es auszuführen. Für die Variante mit Ausnahmefiltern, jedeCatch Klausel verwaltet unabhängig seine SetProjectError/ClearProjectError Aufrufe und so gibt es keine Verwechslung zwischen dem einen für Throw aufgerufen und der für New XElement aufgerufen.