2015-06-12 16 views
7

Dlaczego to powoduje, że BOOM?Niezamierzone konsekwencje podczas zmiany następnego wiersza wykonania w 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

Wyjątek to "Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu". – Robino

+3

Powinieneś dodać to do pytania, a nie w komentarzu. – juharr

+0

Nie można odtworzyć. – Habib

Odpowiedz

3

Patrząc na wyjściu ILDASM, nie może być wyjaśnienie tutaj ...

.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 
    { 

Podczas przeciągania kursora wykonania, to ryzykujemy zgorszenie stos wywołań. Jest tak dlatego, że przeciągnięcie kursora dosłownie pomija te linie. Podczas uruchamiania w debugerze, po naciśnięciu klawisza F10, kursor zatrzymuje się przed uruchomieniem procedury Main przed próbą. Jeśli przeciągnij kursor do utworzenia tablicy, jesteś pomijanie tej zabawy wiersz:

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

który tworzy instancję klasy Program. Klasa program jest następnie wykorzystywany później tutaj:

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

Która bo go pominąć, nie stworzył tego obiektu, więc masz NullReferenceException podczas biegu.

Dlaczego ludzie nie mogą tego odtworzyć na VS2012, nie jestem pewien. Być może kompilator wyprowadza różne IL, ale jest to daleko, jak mogę wymyślić przy użyciu VS2013 Ultimate i C# 4.5.

Co ciekawe, kiedy zakomentuj try/catch, początek programu w IL wygląda tak:

.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 

którym można zobaczyć pierwszą linię w rutynę tworzy obiekt Program. Dlaczego kompilator zdecydował się umieścić tę linię wewnątrz try/catch jest poza mną.

EDIT

Kopiąc głębiej, zmieniając swój program do tego:

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!"); 
     } 
    } 

Wyniki w kod działa. Badając IL, można zobaczyć, że tworzenie instancji został przeniesiony poza Spróbuj:

.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 
    { 

Kompilator był na tyle miły, aby przesunąć utworzenie tablicy ciągów spoza starają się wewnątrz próbie, więc pomijanie tej linii nadal daje prawidłowy obiekt. Kod działa, więc domyślam się, że NullReferenceException jest instancją klasy Program.

+0

Miałem różne działające i niedziałające wersje kodu, ponieważ destylowałem błąd. Na pewno będę korzystał z demontażu, aby odpowiedzieć na te pytania w przyszłości. – Robino

1

Krótko mówiąc, jeśli przeciągnij strzałkę nie ma alokacja pamięci dla zmiennej one więc w zasadzie myślę, że jesteś coraz Reference Null Pointer w ładnym opakowaniu jednostkowym

Funny zdaniem jest, że to samo dzieje się. NET4.0, NET4.5, VS2013 i VS2015 RC, który ma mieć inny kompilator (Roslyn) Zobacz zdjęcia poniżej

Mój zestaw jest bardzo zły, więc nie będę próbował udawać, że wszystko rozumiem to się dzieje.

Zacznij tutaj

enter image description here

Normalne wykonanie

enter image description here

mam podświetlone zmiany macierzy jest zerowy, ale istnieje również widać rejestru zmiany.

nie spojrzeć na to, co się dzieje, kiedy przeciągnij

enter image description here

zmienna nie jest nawet tam (i również wyglądają niemal na zmiany rejestru), więc zgaduję, że masz po prostu pominąłem kilka linii lub montaż.

Teraz możesz także rzucić okiem na pamięć i zobaczyć, że nic tam nie ma.

pamięci kiedy krok koryta

enter image description here

pusta przestrzeń, ale to tam.

czy kroczę koryta go nawet ma wartości

enter image description here

pamięci kiedy przeciągnąć tam

enter image description here

Nic.

Biorąc pod uwagę fakt, że próbowałem z 2 wersjami .NET i 2 różnymi kompilatorami, myślę, że to musi być problem VisualStudio, że możesz opublikować ten błąd. here może zostać załatany w przyszłej aktualizacji.

Powiązane problemy