2015-06-12 6 views
7

Warum geht das BOOM?Unbeabsichtigte Konsequenzen beim Ändern der nächsten Ausführungszeile in Visual Studio

using System; 
using System.Linq; 

namespace Test 
{ 
    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      try 
      { 
       // 1. Hit F10 to step into debugging. 
       string[] one = {"1"}; //2. Drag arrow to make this next statement executed 
       // 3. Hit f5. 
       Enumerable.Range(1,1) 
        .Where(x => one.Contains(x.ToString())); 
      } 
      catch (Exception exception) 
      { 
       Console.Write("BOOM!"); 
      } 
     } 
    } 
} 
+0

Ausnahme ist "Objektreferenz nicht auf eine Instanz eines Objekts festgelegt." – Robino

+3

Sie sollten das zur Frage hinzufügen, nicht in einem Kommentar. – juharr

+0

Kann nicht reproduziert werden. – Habib

Antwort

3

am ILDASM Ausgang sucht, könnte es hier eine Erklärung sein ...

.locals init ([0] class Test.Program/'<>c__DisplayClass1' 'CS$<>8__locals2', 
      [1] class [mscorlib]System.Exception exception, 
      [2] string[] CS$0$0000) 
    IL_0000: nop 
    .try 
    { 
    IL_0001: newobj  instance void Test.Program/'<>c__DisplayClass1'::.ctor() 
    IL_0006: stloc.0 
    IL_0007: nop 
    IL_0008: ldloc.0 
    IL_0009: ldc.i4.1 
    IL_000a: newarr  [mscorlib]System.String 
    IL_000f: stloc.2 
    IL_0010: ldloc.2 
    IL_0011: ldc.i4.0 
    IL_0012: ldstr  "1" 
    IL_0017: stelem.ref 
    IL_0018: ldloc.2 
    IL_0019: stfld  string[] Test.Program/'<>c__DisplayClass1'::one 
    IL_001e: ldc.i4.1 
    IL_001f: ldc.i4.1 
    IL_0020: call  class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, 
                                    int32) 
    IL_0025: ldloc.0 
    IL_0026: ldftn  instance bool Test.Program/'<>c__DisplayClass1'::'<Main>b__0'(int32) 
    IL_002c: newobj  instance void class [mscorlib]System.Func`2<int32,bool>::.ctor(object, 
                         native int) 
    IL_0031: call  class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, 
                                     class [mscorlib]System.Func`2<!!0,bool>) 
    IL_0036: pop 
    IL_0037: nop 
    IL_0038: leave.s IL_004a 
    } // end .try 
    catch [mscorlib]System.Exception 
    { 

Wenn Sie die Ausführung Cursor ziehen, laufen Sie Gefahr, den Call-Stack von korrumpieren. Dies liegt daran, dass beim Ziehen des Cursors diese Zeilen buchstäblich übersprungen werden. Nach dem Drücken von F10 stoppt der Cursor beim Ausführen im Debugger beim Start der Routine Main vor dem Versuch. Wenn Sie den Cursor auf die Erstellung des Arrays ziehen, werden das Überspringen Sie diesen Spaßlinie:

IL_0001: newobj instance void Test.Program/'<>c__DisplayClass1'::.ctor()

, die eine Instanz der Program Klasse erstellt. Die Programmklasse wird dann später hier verwendet:

IL_0019: stfld string[] Test.Program/'<>c__DisplayClass1'::one

Welche weil man sie übersprungen, nicht um das Objekt zu erstellen, so dass Sie ein NullReferenceException bekommen beim Laufen.

Warum Menschen dies nicht auf VS2012 reproduzieren können, bin ich mir nicht sicher. Vielleicht gibt der Compiler unterschiedliche IL aus, aber das ist soweit, wie ich mit VS2013 Ultimate und C# 4.5 kommen kann.

Interessanterweise, wenn Sie die try/catch Kommentar aus, sieht der Start des Programms in IL wie folgt aus:

.locals init ([0] class Test.Program/'<>c__DisplayClass1' 'CS$<>8__locals2', 
      [1] string[] CS$0$0000) 
    IL_0000: newobj  instance void Test.Program/'<>c__DisplayClass1'::.ctor() 
    IL_0005: stloc.0 

dem Sie die erste Zeile in der Routine sehen schafft die Program Objekt. Warum der Compiler entschieden hat, diese Zeile in den try/catch zu setzen, ist mir ein Rätsel.

EDIT

ein wenig tiefer Graben, Ihr Programm dies zu ändern:

private static void Main(string[] args) 
    { 
     string[] one; 

     try 
     { 
      // 1. Hit F10 to step into debugging. 
      one = new string[] { "1" }; //2. Drag arrow to this 
      // 3. Hit f5. 
      Enumerable.Range(1, 1) 
       .Where(x => one.Contains(x.ToString())); 
     } 
     catch (Exception exception) 
     { 
      Console.Write("BOOM!"); 
     } 
    } 

Ergebnisse in Code zu arbeiten. Die Untersuchung des IL, können Sie sehen, dass die Instanz Schöpfung außerhalb des Versuchs verschoben wurde:

.locals init ([0] class [mscorlib]System.Exception exception, 
      [1] class [mscorlib]System.Func`2<int32,bool> 'CS$<>9__CachedAnonymousMethodDelegate1', 
      [2] class Test.Program/'<>c__DisplayClass2' 'CS$<>8__locals3', 
      [3] string[] CS$0$0000) 
    IL_0000: ldnull 
    IL_0001: stloc.1 
    IL_0002: newobj  instance void Test.Program/'<>c__DisplayClass2'::.ctor() 
    IL_0007: stloc.2 
    IL_0008: nop 
    .try 
    { 

Der Compiler schön genug war, von außerhalb des Versuchs, die Schaffung des String-Array zu bewegen, um innerhalb des try, so dass die Linie übersprungen führt immer noch zu einem gültigen Objekt. Der Code funktioniert, also vermute ich, dass die NullReferenceException wirklich die Instanz der Program Klasse ist.

+0

Ich hatte verschiedene funktionierende und nicht funktionierende Versionen des Codes, als ich den Fehler destillierte. Ich werde sicherlich die Demontage verwenden, um diese Fragen in Zukunft zu beantworten. – Robino

1

Lange Rede kurzer Sinn, wenn Sie auf den Pfeil ziehen es keine Speicherzuweisung für die one Variable also im Grunde denke ich, Sie in einem hübschen Wrapper eine Null-Zeiger-Referenz, wenn es darum

Lustig denken, ist, dass das gleiche geschieht in. NET4.0, NET4.5, VS2013 und VS2015 RC, die einen anderen Compiler haben soll (Roslyn) Werfen Sie einen Blick auf die Bilder

Meine Montage ist ziemlich schlecht, also werde ich nicht versuchen, so zu tun, alles zu verstehen das geht weiter.

starten hier

enter image description here

Normale Ausführung

enter image description here

ich die Änderungen markiert haben Ihr Array ist null, aber es existiert, auch können Sie die Registrierung sehen Änderungen.

nicht einen Blick auf das, was passiert, wenn ich ziehen

enter image description here

Ihre Variable nicht einmal da ist (und fast auch an den Registry-Änderungen sehen), so vermute ich, dass Sie habe nur ein paar Zeilen oder Assembly übersprungen.

Jetzt können Sie auch einen Blick auf die Erinnerung werfen und sehen, dass da nichts ist.

Speicher, wenn ich Schritt Trog

enter image description here

leeren Raum, aber es ist da.

Wenn ich es Schritt Trog hat sogar Werte

enter image description here

Speicher, wenn ich ziehen

enter image description here

Nichts da.

Angesichts der Tatsache, dass ich mit 2 Versionen von .NET und 2 verschiedenen Compilern versuchte ich denke, es muss ein VisualStudio-Problem sein, Sie könnten diesen Fehler here posten, es könnte in einem zukünftigen Update gepatcht werden.

Verwandte Themen