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 ildasm
und bearbeiten. Beachten Sie diese Zeile:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
Ursprünglich war das callvirt
statt 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 callvirt
zu 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 callvirt
fü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.