CLR System.NullReferenceException beim Erzwingen von 'Set Next Statement' in 'if' -Block
Ich akzeptiere, dass dies nicht während der normalen Codeausführung auftreten kann, aber ich habe es beim Debuggen entdeckt und fand es interessant, es zu teilen.
Ich denke, das liegt am JIT-Compiler, würde mich aber über weitere Überlegungen freuen.
Ich habe dieses Problem mit VS2013 für das 4.5- und 4.5.1-Framework repliziert:
InstalliereUm diese Ausnahme zu sehenCommon Language Runtime Exceptions
muss aktiviert sein:DEBUG
> Exceptions...
Ich habe die Ursache des Problems in folgendem Beispiel zusammengefasst:
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication6
{
public class Program
{
static void Main()
{
var myEnum = MyEnum.Good;
var list = new List<MyData>
{
new MyData{ Id = 1, Code = "1"},
new MyData{ Id = 2, Code = "2"},
new MyData{ Id = 3, Code = "3"}
};
// Evaluates to false
if (myEnum == MyEnum.Bad) // BREAK POINT
{
/*
* A first chance exception of type 'System.NullReferenceException' occurred in ConsoleApplication6.exe
Additional information: Object reference not set to an instance of an object.
*/
var x = new MyClass();
MyData result;
//// With this line the 'System.NullReferenceException' gets thrown in the line above:
result = list.FirstOrDefault(r => r.Code == x.Code);
//// But with this line, with 'x' not referenced, the code above runs ok:
//result = list.FirstOrDefault(r => r.Code == "x.Code");
}
}
}
public enum MyEnum
{
Good,
Bad
}
public class MyClass
{
public string Code { get; set; }
}
public class MyData
{
public int Id { get; set; }
public string Code { get; set; }
}
}
Zum ReplizierenLegen Sie einen Haltepunkt aufif (myEnum == MyEnum.Bad)
und führen Sie den Code aus. Wenn der Haltepunkt erreicht ist, wirdSet Next Statement
( Strg+Verschiebun+ F10) als erste Klammer desif
Anweisung und laufe bis:
Nächste, Kommentarau die erste Lamda-Anweisung und Kommentari das zweite - so dasMyClass
Instanz wird nicht verwendet. Führe den Prozess erneut aus (triff die Pause, erzwinge dasif
Anweisung und Laufen). Sie werden sehen, dass der Code korrekt funktioniert:
Schließlich Kommentari die erste Lamda-Anweisung und Kommentarau das zweite - so dasMyClass
instance ist benutzt. Dann überarbeiten Sie den Inhalt desif
Anweisung in eine neue Methode:
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication6
{
public class Program
{
static void Main()
{
var myEnum = MyEnum.Good;
var list = new List<MyData>
{
new MyData{ Id = 1, Code = "1"},
new MyData{ Id = 2, Code = "2"},
new MyData{ Id = 3, Code = "3"}
};
// Evaluates to false
if (myEnum == MyEnum.Bad) // BREAK POINT
{
MyMethod(list);
}
}
private static void MyMethod(List<MyData> list)
{
// When the code is in this method, it works fine
var x = new MyClass();
MyData result;
result = list.FirstOrDefault(r => r.Code == x.Code);
}
}
public enum MyEnum
{
Good,
Bad
}
public class MyClass
{
public string Code { get; set; }
}
public class MyData
{
public int Id { get; set; }
public string Code { get; set; }
}
}
Führen Sie den Test erneut aus und alles funktioniert korrekt:
FazitIch gehe davon aus, dass der JIT-Compiler das lamda so optimiert hat, dass es immer null ist, und dass vor der Initialisierung der Instanz ein weiter optimierter Code ausgeführt wird.
Wie ich bereits erwähnt habe, könnte dies im Produktionscode niemals passieren, aber ich wäre gespannt, was passiert.