C# для проверки равенства

каков ваш подход к написанию проверок равенства для structs и classes создать?

1) "полная" проверка равенства требует, чтобы большая часть шаблонного кода (например,override Equals, override GetHashCode, generic Equals, operator==, operator!=)?

2) вы явно указываете, что ваши классы моделируют IEquatable<T> интерфейс?

3) правильно ли я понимаю, что нет фактический способ автоматического применения Equals переопределяет, когда я вызываю что-то вроде a == b и я всегда должен реализовывать как Equals и operator== члены?

4 ответов


вы правы, это много кода котельной плиты, и вам нужно реализовать все отдельно.

Я бы порекомендовал:

  • если вы собираетесь реализовать равенство значений вообще, переопределите GetHashCode и Equals(object) - создание перегрузок для == и реализация IEquatable<T> без этого может привести к очень неожиданному поведению
  • я бы всегда реализовывал IEquatable<T> если вы переопределяете Equals(object) и GetHashCode
  • я только перегрузите оператор == реже
  • правильно реализовать равенство для незапечатанных классов сложно и все еще может привести к неожиданным / нежелательным результатам. Если вам нужно равенство для типов в иерархии, реализуйте IEqualityComparer<T> выражая сравнение, которое вас интересует.
  • равенство для изменяемых типов обычно является плохой идеей, так как два объекта могут быть равными, а затем неравными позже... если объект мутирует (влияет на равенство) после того, как он был использован в качестве ключа в хэш-таблица, вы не сможете найти ее снова.
  • некоторая из котельной плиты немножко различна для структур... но, как и Марк, я очень редко пишу свои собственные структуры.

вот пример реализации:

using System;

public sealed class Foo : IEquatable<Foo>
{
    private readonly string name;
    public string Name { get { return name; } }

    private readonly int value;
    public int Value { get { return value; } }

    public Foo(string name, int value)
    {
        this.name = name;
        this.value = value;
    }

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

    public override int GetHashCode()
    {
        int hash = 17;
        hash = hash * 31 + (name == null ? 0 : name.GetHashCode());
        hash = hash * 31 + value;
        return hash;
    }

    public bool Equals(Foo other)
    {
        if ((object) other == null)
        {
            return false;
        }
        return name == other.name && value == other.value;
    }

    public static bool operator ==(Foo left, Foo right)
    {
        return object.Equals(left, right);
    }

    public static bool operator !=(Foo left, Foo right)
    {
        return !(left == right);
    }
}

и да, это чертовски много шаблонных, очень мало из которых изменяется между реализациями : (

реализация == is немного менее эффективно, чем это может быть, как это будет позвоните в Equals(object) который должен выполнить проверку динамического типа... но альтернатива еще более котельная плита, как это:

public static bool operator ==(Foo left, Foo right)
{
    if ((object) left == (object) right)
    {
        return true;
    }

    // "right" being null is covered in left.Equals(right)
    if ((object) left == null)
    {
        return false;
    }
    return left.Equals(right);
}

Я редко делаю что-то особенное для классов; для большинства регулярных объектов референтное равенство отлично работает.

Я еще реже пишу struct; но так как структуры представляют значения как правило, целесообразно обеспечить равенство и т. д. Это обычно все равно, ==, != и IEquatable<T> (так как это позволяет избежать бокса в сценариях с использованием EqualityComparer<T>.Default.

шаблон обычно не слишком проблематичен, но инструменты IIRC, такие как resharper, могут помочь здесь.

Да, рекомендуется сохранять равные и == в синхронизации, и это должно быть сделано явно.


вам просто нужно реализовать operator== для a==b .
Поскольку мне нравятся мои данные в словарях, иногда я переопределяю GetHashCode.
далее я реализую Equals (как не упомянутый стандарт ... это связано с тем, что при использовании дженериков нет ограничений для равенства) и укажите реализацию IEquatable. Поскольку я собираюсь сделать это, я мог бы также указать my == and != реализации равны. :)


посмотреть что такое "лучшая практика" для сравнения двух экземпляров ссылочного типа?

вы можете избежать кода котельной плиты (надеюсь, что C#/VS team приносит что-то легкое для разработчиков в их следующей итерации) с помощью фрагмента, вот один из таких..