Написание хорошего метода равных С#

Есть ли у кого-нибудь шаблон для написания приличного метода equals - я помню, что в Effective Java были проблемы с обработкой равных при работе с подклассами.

У меня нет книги со мной, и я не могу вспомнить, было ли это практическим советом. Итак, как вы пишете надежную реализацию метода равных равных?

Ответ 1

Возможно, это предложение вне стены, но: во-первых, не следует переопределять Equals. В принципе, принцип равенства не работает с подклассом, как вы упомянули. Однако почти везде в .NET API, который использует равенство (например, словари, хэш-множества), разрешается передавать IEqualityComparer<T>. Создание другого объекта, ответственного за равенство, делает жизнь намного более гибкой: вы можете использовать разные объекты, чтобы определить, какие критерии использования.

Реализация IEqualityComparer<T> намного проще - вам все равно нужно проверить недействительность, но вам не нужно беспокоиться о том, подходят ли типы или будет ли Equals еще более переопределено.

Еще один подход к обеспечению нормальной работы Equals заключается в том, чтобы избежать наследования полностью по большей части - я не могу вспомнить последний раз, когда в моем коде действительно имело смысл переопределить Equals и разрешить производные классы. sealed FTW:)

Ответ 2

Возможно, вы уже это сделали, но просмотрели ли вы статью MSDN при реализации Equals()?

Внедрение метода Equals

Ответ 3

Свойства метода хороших равных:

  • Симметрия. Для двух ссылок a и b, a.equals(b) тогда и только тогда, когда b.equals(a)
  • Рефлексивность. Для всех непустых ссылок a.equals(a)
  • Транзитивность: если a.equals(b) и b.equals(c), то a.equals(c)

Ответ 4

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

public abstract class ValueObject<T> : IEquatable<T>
    where T : ValueObject<T>
{
    protected abstract IEnumerable<object> Reflect();

    public override bool Equals(Object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != GetType()) return false;
        return Equals(obj as T);
    }

    public bool Equals(T other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Reflect().SequenceEqual(other.Reflect());
    }

    public override int GetHashCode()
    {
        return Reflect().Aggregate(36, (hashCode, value) => value == null ?
                                hashCode : hashCode ^ value.GetHashCode());
    }

    public override string ToString()
    {
        return "{ " + Reflect().Aggregate((l, r) => l + ", " + r) + " }";
    }
}

Теперь, чтобы создать класс, подобный значению, просто скажите:

public class Person : ValueObject<Person>
{
    public int Age { get; set; }
    public string Name { get; set; }

    protected override IEnumerable<object> Reflect()
    {
        return new object[] { Age, Name };
    }
}

В Reflect override вы возвращаете последовательность значений, которые должны способствовать равенству.

К сожалению, этот подход не может помочь в объявлении operator ==, поскольку это должно быть специально объявлено на производном типе.

Ответ 6

Обычно я делаю что-то вроде этого:

public struct EmailAddress : IEquatable<EmailAddress>
{
    public override bool Equals(object obj)
    {
        return obj != null && obj.GetType() == typeof(EmailAddress) && Equals((EmailAddress)obj);
    }

    public bool Equals(EmailAddress other)
    {
        return this.LocalPart == other.LocalPart && this.Domain == other.Domain;
    }
}

Ответ 7

"Хороший" метод равен метод, который сравнивает уникальную часть класса, оставляя вне части, которые не вносят вклад в уникальность. Итак, если у вас есть класс с уникальным идентификатором, вы можете просто использовать его для установления равенства, не оставляя никаких других свойств. Часто вам также потребуется некоторое значение времени.

Ответ 8

публичный класс Person   {       public string Имя {get; задавать; }

    public int Age { get; set; }

    public override bool Equals(object obj)
    {
        var objAsPerson = obj as Person;

        if (obj == null)
        {
            return false;
        }

        if (this.Name != objAsPerson.Name)
        {
            return false;
        }

        if (this.Age != objAsPerson.Age)
        {
            return false;
        }

        return true;
    }
}