Ich nehme an, Sie haben sich die .NET 3.5-Implementierung angesehen. Ich glaube, die .NET 4-Implementierung ist etwas anders.
Ich habe jedoch den Verdacht, dass dies daran liegt, dass sogar virtuelle Instanzmethoden nicht virtuell für eine Nullreferenz aufgerufen werden können . Möglich in IL, das heißt. Ich werde sehen, ob ich IL produzieren kann, die anrufen würde null.Equals(null).
EDIT: Okay, hier ist ein interessanter Code:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (string V_0)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldnull
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
IL_000a: call void [mscorlib]System.Console::WriteLine(bool)
IL_000f: nop
IL_0010: ret
}
Ich habe dies durch Kompilieren des folgenden C # -Codes erhalten:
using System;
class Test
{
static void Main()
{
string x = null;
Console.WriteLine(x.Equals(null));
}
}
... und dann zerlegen ildasmund bearbeiten. Beachten Sie diese Zeile:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
Ursprünglich war das callvirtstatt call.
Was passiert also, wenn wir es wieder zusammenbauen? Nun, mit .NET 4.0 bekommen wir Folgendes:
Unhandled Exception: System.NullReferenceException: Object
reference not set to an instance of an object.
at Test.Main()
Hmm. Was ist mit .NET 2.0?
Unhandled Exception: System.NullReferenceException: Object reference
not set to an instance of an object.
at System.String.EqualsHelper(String strA, String strB)
at Test.Main()
Das ist interessanter ... wir haben es eindeutig geschafft, uns darauf einzulassen EqualsHelper, was wir normalerweise nicht erwartet hätten.
Genug der Zeichenfolge ... Lassen Sie uns versuchen, die Referenzgleichheit selbst zu implementieren und zu prüfen, ob wir null.Equals(null)true zurückgeben können:
using System;
class Test
{
static void Main()
{
Test x = null;
Console.WriteLine(x.Equals(null));
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override bool Equals(object other)
{
return other == this;
}
}
Die gleiche Prozedur wie vor - auseinanderbauen, ändern callvirtzu call, wieder zusammenbauen, und beobachten Sie es drucken true...
Beachten Sie, dass, obwohl eine andere Antwort auf diese C ++ - Frage verweist , wir hier noch hinterhältiger sind ... weil wir eine virtuelle Methode nicht virtuell aufrufen . Normalerweise wird sogar der C ++ / CLI-Compiler callvirtfür eine virtuelle Methode verwendet. Mit anderen Worten, ich denke in diesem speziellen Fall besteht die einzige Möglichkeit this, Null zu sein, darin, die IL von Hand zu schreiben.
EDIT: Ich habe gerade etwas bemerkt ... Ich habe in keinem unserer kleinen Beispielprogramme die richtige Methode aufgerufen. Hier ist der Anruf im ersten Fall:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
Hier ist der Anruf im zweiten:
IL_0005: call instance bool [mscorlib]System.Object::Equals(object)
Im ersten Fall, ich meinte zu Anruf System.String::Equals(object), und in der zweiten, ich soll anrufen Test::Equals(object). Daraus können wir drei Dinge erkennen:
- Sie müssen mit Überlastung vorsichtig sein.
- Der C # -Compiler sendet Aufrufe an den Deklarator der virtuellen Methode - nicht die spezifischste Überschreibung der virtuellen Methode. IIRC, VB arbeitet umgekehrt
object.Equals(object) freut sich, eine Null "diese" Referenz zu vergleichen
Wenn Sie der C # -Überschreibung ein wenig Konsolenausgabe hinzufügen, können Sie den Unterschied erkennen. Sie wird nur aufgerufen, wenn Sie die IL so ändern, dass sie explizit aufgerufen wird:
IL_0005: call instance bool Test::Equals(object)
Da sind wir also. Spaß und Missbrauch von Instanzmethoden bei Nullreferenzen.
Wenn Sie es bis hierher geschafft haben, möchten Sie vielleicht auch meinen Blog-Beitrag darüber lesen, wie Werttypen parameterlose Konstruktoren deklarieren können ... in IL.