Оператор перегрузки == против Equals()

Я работаю над проектом C#, в котором до сих пор я использовал неизменяемые объекты и фабрики, чтобы гарантировать, что объекты типа Foo всегда можно сравнить на равенство с ==,

Foo объекты не могут быть изменены после создания, и фабрика всегда возвращает один и тот же объект для заданного набора аргументов. Это прекрасно работает, и во всей кодовой базе мы предполагаем, что == всегда работает для проверки равенства.

Теперь мне нужно добавить некоторые функциональные возможности, которые вводят крайний случай, для которого это не всегда работает. Самое простое, что нужно сделать, это перегрузить operator == для этого типа, так что ни один другой код в проекте не должен изменяться. Но это кажется мне запахом кода: перегрузка operator == и не Equals просто кажется странным, и я привык к соглашению, что == проверяет равенство ссылок и Equals проверяет равенство объектов (или любой другой термин).

Это законное беспокойство, или я должен просто пойти дальше и перегрузить operator ==?

6 ответов

Решение

Я считаю, что стандартом является то, что для большинства типов.Equals проверяет сходство объектов и оператор == проверяет равенство ссылок.

Я считаю, что лучшая практика заключается в том, что для неизменяемых типов оператор == следует проверять на сходство, а также .Equals, И если вы хотите узнать, действительно ли это один и тот же объект, используйте .ReferenceEquals, Смотрите C# String класс для примера этого.

Существует большая разница между перегрузкой == и переопределение равно.

Когда у вас есть выражение

if (x == y) {

Метод, который будет использоваться для сравнения переменных x и y, определяется во время компиляции. Это перегрузка оператора. Тип, используемый при объявлении x и y, используется для определения того, какой метод используется для их сравнения. Фактический тип в пределах x и y (то есть, реализация подкласса или интерфейса) не имеет значения. Учтите следующее.

object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference

if (x == y) { // evaluates to FALSE

и следующее

string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference

if (x == y) { // evaluates to TRUE

Это показывает, что тип, используемый для объявления переменных x и y, используется для определения того, какой метод используется для оценки ==.

Для сравнения, Equals определяется во время выполнения на основе фактического типа в переменной x. Equals - это виртуальный метод объекта, который другие типы могут и могут переопределять. Поэтому оба следующих примера оценивают как истинные.

object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference

if (x.Equals(y)) { // evaluates to TRUE

и следующее

string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference

if (x.Equals(y)) { // also evaluates to TRUE

Это определенно пахнет. При перегрузке == Вы должны убедиться, что оба Equals() а также GetHashCode() также последовательны. См. Рекомендации MSDN.

И единственная причина, по которой это кажется нормальным - это то, что вы описываете свой тип как неизменяемый.

Пример, показывающий, как реализовать это в соответствии с рекомендациями MSFT (ниже). Обратите внимание, что при переопределении Equals вам также необходимо переопределить GetHashCode(). Надеюсь, что это помогает людям.

public class Person
{
    public Guid Id { get; private set; }

    public Person(Guid id)
    {
        Id = id;
    }

    public Person()
    {
        Id = System.Guid.NewGuid();
    }

    public static bool operator ==(Person p1, Person p2)
    {
        bool rc;

        if (System.Object.ReferenceEquals(p1, p2))
        {
            rc = true;
        }
        else if (((object)p1 == null) || ((object)p2 == null))
        {
            rc = false;
        }
        else
        {
            rc = (p1.Id.CompareTo(p2.Id) == 0);
        }

        return rc;
    }

    public static bool operator !=(Person p1, Person p2)
    {
        return !(p1 == p2);
    }

    public override bool Equals(object obj)
    {
        bool rc = false;
        if (obj is Person)
        {
            Person p2 = obj as Person;
            rc = (this == p2);
        }
        return rc;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Для неизменяемых типов я не думаю, что что-то не так с == перегружен для поддержки равенства стоимости. Я не думаю, что я переопределил бы == без переопределения Equals иметь ту же семантику, однако. Если вы переопределите == и нужно проверить равенство ссылок по какой-то причине, вы можете использовать Object.ReferenceEquals(a,b),

Смотрите эту статью Microsoft для некоторых полезных рекомендаций

Согласно собственным рекомендациям Microsoft, результат метода Equals и перегрузки equals (==) должны быть одинаковыми.

CA2224: переопределение равно при перегрузке оператора равно

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