Проверка соответствия С#

Какой ваш подход при написании проверок равенства для создаваемых structs и classes?

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

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

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

Ответ 1

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

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

  • Если вы собираетесь вообще реализовать равенство значений, переопределите 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);
    }
}

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

Реализация == немного менее эффективна, чем могла бы быть, так как она вызовет 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);
}

Ответ 2

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

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

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

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

Ответ 3

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