Полагаю, вы рассматривали реализацию .NET 3.5? Я считаю, что реализация .NET 4 немного отличается.
Однако у меня есть подозрение, что это связано с тем, что даже виртуальные методы экземпляра можно вызывать не виртуально по нулевой ссылке . Возможно в ИЛ, то есть. Я посмотрю, смогу ли я создать какой-нибудь IL, который будет называться null.Equals(null)
.
РЕДАКТИРОВАТЬ: Хорошо, вот несколько интересных кодов:
.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
Я получил это, скомпилировав следующий код C #:
using System;
class Test
{
static void Main()
{
string x = null;
Console.WriteLine(x.Equals(null));
}
}
... а затем разборка с ildasm
и редактирование. Обратите внимание на эту строку:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
Первоначально это было callvirt
вместо call
.
Итак, что происходит, когда мы собираем его? Ну, с .NET 4.0 мы получаем это:
Unhandled Exception: System.NullReferenceException: Object
reference not set to an instance of an object.
at Test.Main()
Хм. А как насчет .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()
Теперь это более интересно ... нам явно удалось попасть в EqualsHelper
, чего мы обычно не ожидали.
Достаточно строки ... давайте попробуем сами реализовать равенство ссылок и посмотрим, сможем ли мы получить null.Equals(null)
, чтобы вернуть 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;
}
}
Та же процедура, что и раньше - разобрать, изменить callvirt
на call
, собрать и посмотреть, как это печатается true
...
Обратите внимание, что хотя другой ответ ссылается на этот вопрос C ++ , мы здесь еще более коварны ... потому что мы вызываем виртуальный метод не виртуально. Обычно даже компилятор C ++ / CLI будет использовать callvirt
для виртуального метода. Другими словами, я думаю, что в этом конкретном случае единственный способ для this
быть нулевым - это написать IL вручную.
РЕДАКТИРОВАТЬ: Я только что заметил что-то ... Я на самом деле не вызывал правильный метод в или наших маленьких примеров программ. Вот звонок в первом случае:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
вот звонок во втором:
IL_0005: call instance bool [mscorlib]System.Object::Equals(object)
В первом случае I означал для вызова System.String::Equals(object)
, а во втором I означал для вызова Test::Equals(object)
. Отсюда видно три вещи:
- Вы должны быть осторожны с перегрузкой.
- Компилятор C # отправляет вызовы объявителю виртуального метода - не самое специфическое переопределение виртуального метода. IIRC, VB работает наоборот
object.Equals(object)
рад сравнить нулевую ссылку "this"
Если вы добавите немного консольного вывода в переопределение C #, вы увидите разницу - он не будет вызываться, если вы не измените IL для его явного вызова, например:
IL_0005: call instance bool Test::Equals(object)
Итак, мы здесь. Веселье и злоупотребление методами экземпляра на нулевых ссылках.
Если вы продвинулись так далеко, вы также можете посмотреть в моем блоге сообщение о , как типы значений могут объявить конструкторы без параметров ... в IL.