Как реализовать IEqualityComparer в неизменяемой общей структуре Pair?

StackOverflow https://stackoverflow.com/questions/120783

  •  02-07-2019
  •  | 
  •  

Вопрос

В настоящее время у меня есть это (отредактировано после прочтения совета):

struct Pair<T, K> : IEqualityComparer<Pair<T, K>>
{
    readonly private T _first;
    readonly private K _second;

    public Pair(T first, K second)
    {
        _first = first;
        _second = second;

    }

    public T First { get { return _first; } }
    public K Second { get { return _second; } }

    #region IEqualityComparer<Pair<T,K>> Members

    public bool Equals(Pair<T, K> x, Pair<T, K> y)
    {
        return x.GetHashCode(x) == y.GetHashCode(y);
    }

    public int GetHashCode(Pair<T, K> obj)
    {
        int hashCode = obj.First == null ? 0 : obj._first.GetHashCode();

        hashCode ^= obj.Second == null ? 0 : obj._second.GetHashCode();

        return hashCode;
    }

    #endregion

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

    public override bool Equals(object obj)
    {
        return (obj != null) && 
    (obj is Pair<T, K>) && 
    this.Equals(this, (Pair<T, K>) obj);
    }
}

Проблема в том, что First и Second могут не быть ссылочными типами (VS вообще-то меня об этом предупреждает), но код все равно компилируется.Должен ли я приводить их (первый и второй) к объектам, прежде чем сравнивать их, или есть лучший способ сделать это?

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

Редактировать 2:Что касается того, чего я пытаюсь достичь, я хочу, чтобы это работало в словаре.Во-вторых, SRP для меня сейчас не важен, потому что на самом деле это не суть этой проблемы — его всегда можно отрефакторить позже.В-третьих, сравнение с default(T) не будет работать вместо сравнения с null — попробуйте.

Это было полезно?

Решение

Похоже, вам нужен IEquatable:

internal struct Pair<T, K> : IEquatable<Pair<T, K>>
{
  private readonly T _first;
  private readonly K _second;

  public Pair(T first, K second)
  {
    _first = first;
    _second = second;
  }

  public T First
  {
    get { return _first; }
  }

  public K Second
  {
    get { return _second; }
  }

  public bool Equals(Pair<T, K> obj)
  {
    return Equals(obj._first, _first) && Equals(obj._second, _second);
  }

  public override bool Equals(object obj)
  {
    return obj is Pair<T, K> && Equals((Pair<T, K>) obj);
  }

  public override int GetHashCode()
  {
    unchecked
    {
      return (_first != null ? _first.GetHashCode() * 397 : 0) ^ (_second != null ? _second.GetHashCode() : 0);
    }
  }
}

Другие советы

Ваша реализация IEqualityComparer должна быть другим классом (и определенно не структурой, поскольку вы хотите повторно использовать ссылку).

Кроме того, ваш хэш-код никогда не должен кэшироваться, поскольку реализация GetHashcode по умолчанию для структуры (которую вы не переопределяете) будет учитывать этот член.

Если вы используете хеш-коды при сравнении методов, вам следует проверить «реальное значение», если хеш-коды совпадают.

bool result = ( x._hashCode == y._hashCode );
if ( result ) { result = ( x._first == y._first && x._second == y._second ); }
// OR?: if ( result ) { result = object.Equals( x._first, y._first ) && object.Equals( x._second, y._second ); }
// OR?: if ( result ) { result = object.ReferenceEquals( x._first, y._first ) && object.Equals( x._second, y._second ); }
return result;

Но есть небольшая проблема со сравнением полей «_first» и «_ Second».По умолчанию ссылочные типы используют метод сравнения предварительного равенства «object.ReferenceEquals», но они могут их переопределить.Таким образом, правильное решение зависит от того, «что именно должен делать» ваш метод сравнения.Следует ли использовать метод «Equals» для полей «_first» и «_ Second» или object.ReferenceEquals?Или что-то более сложное?

Что касается предупреждения, вы можете использовать default(T) и default(K) вместо null.

Я не понимаю, чего вы пытаетесь достичь, но вам не следует использовать хэш-код для сравнения на равенство — нет гарантии, что два разных объекта не будут иметь одинаковый хэш-код.Кроме того, хотя ваша структура является неизменяемой, члены _first и _ Second таковыми не являются.

Прежде всего, этот код нарушает принцип SRP.Класс Pair используется для хранения пар предметов, верно?Неправильно делегировать ему функциональность сравнения равенства.

Далее давайте посмотрим на ваш код:

Метод Equals завершится неудачей, если один из аргументов равен нулю — это бесполезно.Equals использует хеш-код класса Pair, но взгляните на определение GetHashCode, это просто комбинация хэш-кодов членов пары - это не имеет ничего общего с равенством элементов.Я ожидаю, что метод Equals будет сравнивать фактические данные.К сожалению, сейчас я слишком занят, чтобы обеспечить правильную реализацию.Но на первый взгляд ваш код кажется неправильным.Будет лучше, если вы предоставите нам описание того, чего вы хотите достичь.Я уверен, что члены SO смогут дать вам несколько советов.

Могу ли я предложить использовать лямбда-выражения в качестве параметра?это позволит вам указать, как сравнивать внутренние универсальные типы.

Я не получаю никаких предупреждений при компиляции об этом, но я предполагаю, что вы говорите о сравнении == null?Кажется, что актерский состав сделал бы все это немного чище, да.

ПС.Вам действительно следует использовать отдельный класс для компаратора.Этот класс, выполняющий две роли (составление пары и сравнение пар), просто уродлив.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top