Зачем мне когда-либо хотеть сделать object.ReferenceEquals(null, this) в переопределении Equals?

Рассмотрим следующий код, который я просматривал:

public override bool Equals(object other)
{
    return !object.ReferenceEquals(null, this)
           && (object.ReferenceEquals(this, other)
           || ((other is MyType) && this.InternalEquals((MyType)other)));
}

Первая строка в этом коде вызвала мое любопытство. Всякий раз, когда this равен нулю, метод должен возвращать false. Теперь я уверен, что программист хотел написать !object.ReferenceEquals(other, null), чтобы сочетать ситуации с null, но он настаивает на том, что this может быть нулевым Я настаиваю на том, что это невозможно (если кто-то не использует прямую манипуляцию с памятью). Должны ли мы оставить это в?

2 ответа

Решение

Хотя я, конечно, не проверю this для нуля, это возможно, без какой-либо реальной злобности памяти - просто немного размышлений:

using System;

public class Test
{
    public void CheckThisForNullity()
    {
        Console.WriteLine("Is this null? {0}", this == null);
    }

    static void Main(string[] args)
    {
        var method = typeof(Test).GetMethod("CheckThisForNullity");
        var openDelegate = (Action<Test>) Delegate.CreateDelegate(
               typeof(Action<Test>), method);
        openDelegate(null);
    }
}

Кроме того, генерировать IL, который использует call вместо callvirt вызвать метод экземпляра для нулевой цели. Вполне законно, просто не то, что обычно делает компилятор C#.

Это не имеет ничего общего с завершением, которое само по себе волосатое, но по-разному. Финализатор может запускаться во время выполнения метода экземпляра, если CLR может доказать, что вы не собираетесь использовать какие-либо поля в экземпляре (что я бы настоятельно ожидал включить this ссылка).

Что касается представленного кода - нет, похоже, это просто ошибка. Я бы переписал это как:

public override bool Equals(object other)
{
    return Equals(other as MyType);
}

public bool Equals(MyType other)
{
    if (ReferenceEquals(other, null))
    {
        return false;
    }
    // Now perform the equality check
}

... при условии, что MyType это класс, а не структура. Обратите внимание, как я использую другой публичный метод с правильным типом параметра - я бы реализовал IEquatable<MyType> в то же время.

C# обычно не позволяет вызывать методы null, Я думаю, что программист, который написал это, происходит из C++ (где, я думаю, можно вызывать методы nullдо тех пор, пока они не имеют доступа к элементу данных this) или писать с защитой для особых сценариев (таких как вызов отражением, как уже было сказано).

Другие вопросы по тегам