Как мне реализовать IEqualityComparer в неизменяемой общей структуре Pair? - PullRequest
3 голосов
/ 23 сентября 2008

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

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 фактически предупреждает меня об этом), но код все еще компилируется. Должен ли я привести их (Первый и Второй) к объектам, прежде чем сравнивать их, или есть лучший способ сделать это?

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

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

Ответы [ 7 ]

2 голосов
/ 23 сентября 2008

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

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

2 голосов
/ 23 сентября 2008

Похоже, вам нужен 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);
    }
  }
}
1 голос
/ 23 сентября 2008

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

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", но они могут их переопределить. Таким образом, правильное решение зависит от того, «что именно должен делать» метод сравнения. Следует ли использовать метод «Равные» полей «_first» и «_second» или object.ReferenceEquals? Или что-то более сложное?

0 голосов
/ 23 сентября 2008

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

PS. Вы действительно должны использовать отдельный класс для сравнения. Этот класс, который выполняет две роли (будучи парой и сравнивая пары), просто уродлив.

0 голосов
/ 23 сентября 2008

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

0 голосов
/ 23 сентября 2008

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

Далее, давайте взглянем на ваш код:

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

0 голосов
/ 23 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...