Supongo que estabas viendo la implementación .NET 3.5? Creo que la implementación de .NET 4 es ligeramente diferente.
Sin embargo, tengo la sospecha de que esto se debe a que es posible llamar incluso a los métodos de instancia virtual no virtualmente en una referencia nula. Posible en IL, eso es. Veré si puedo producir algunos IL que llamarían al null.Equals(null)
.
EDIT: Bueno, aquí hay un código interesante:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 17 (0x11)
.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
} // end of method Test::Main
Tengo esta compilando el siguiente código C#:
using System;
class Test
{
static void Main()
{
string x = null;
Console.WriteLine(x.Equals(null));
}
}
... y luego desmontar con ildasm
y edición. Tenga en cuenta esta línea:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
Originalmente, era callvirt
en lugar de call
.
Entonces, ¿qué sucede cuando lo montamos de nuevo? Bueno, con .NET 4.0 obtenemos esto:
Unhandled Exception: System.NullReferenceException: Object
reference not set to an instance of an object.
at Test.Main()
Hmm. ¿Qué pasa con .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()
Ahora que es más interesante ... claramente que hemos conseguido entrar en EqualsHelper
, que no habríamos esperado normalmente.
Basta de cadena ... vamos a tratar de poner en práctica la igualdad referencia a nosotros mismos y ver si podemos conseguir null.Equals(null)
a devolver true:
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;
}
}
El mismo procedimiento que antes - desmonte, cambie callvirt
-call
, volver a montar, y verlo imprimir true
...
en cuenta que aunque otras respuestas Referencias de this C++ question, estamos siendo aún más tortuosa aquí ... porque estamos llamando a un método no virtual -virtualmente. Normalmente incluso el compilador C++/CLI usará callvirt
para un método virtual. En otras palabras, creo que en este caso particular, la única forma de que this
sea nulo es escribir el IL manualmente.
EDIT: acabo de cuenta de algo ... yo no estaba realmente llamando al método justo en cualquiera de nuestros programas de muestra pequeños. Aquí está la llamada en el primer caso:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
aquí está la llamada en el segundo:
IL_0005: call instance bool [mscorlib]System.Object::Equals(object)
En el primer caso, decir llamar System.String::Equals(object)
, y en el segundo, que significaba para llamar al Test::Equals(object)
. De esto podemos ver tres cosas:
- Debe tener cuidado con la sobrecarga.
- El compilador de C# emite llamadas al declarante del método virtual - no es el más específico el que reemplaza el del método virtual. IIRC, VB funciona de la manera opuesta
object.Equals(object)
es feliz para comparar un valor nulo "este" referencia
Si agrega un poco de salida de la consola a la C# anulación, se puede ver la diferencia - no lo hará ser llamado a menos que cambie el IL para llamarlo explícitamente, así:
IL_0005: call instance bool Test::Equals(object)
Entonces, ahí estamos. Diversión y abuso de métodos de instancia en referencias nulas.
Si ha llegado hasta aquí, también puede consultar mi publicación de blog sobre how value types can declare parameterless constructors ... en IL.
¿Puedes echar un vistazo a EqualsHelper también? Parece que querían usar EqualsHelper, pero es posible que no maneje los valores nulos de la forma que quisieran. –
Esto es especialmente interesante, ya que la documentación establece explícitamente que Equals lanzará una NullReferenceException si la instancia es nula .... – womp
Supongo que es un descuido o tiene algo que ver con el funcionamiento de 'EqualsHelper'. Realmente no puedo ver la necesidad de esa declaración 'if' en absoluto, suponiendo que' EqualsHelper' devolvería 'false' cuando' strB' es 'null' y' this' no. Pero tal vez no soy lo suficientemente inteligente como para entender :) –