Что необходимо переопределить в структуре, чтобы обеспечить правильное функционирование равенства?

Как видно из названия: мне нужно переопределить == оператор? как насчет .Equals() метод? Что-то я пропускаю?

6 ответов

Решение

Пример из MSDN

public struct Complex 
{
   double re, im;
   public override bool Equals(Object obj) 
   {
      return obj is Complex && this == (Complex)obj;
   }
   public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }
   public static bool operator ==(Complex x, Complex y) 
   {
      return x.re == y.re && x.im == y.im;
   }
   public static bool operator !=(Complex x, Complex y) 
   {
      return !(x == y);
   }
}

Вы также должны реализовать IEquatable. Вот выдержка из Руководства по проектированию платформы:

Реализуйте IEquatable для типов значений. Метод Object.Equals для типов значений вызывает упаковку, и его реализация по умолчанию не очень эффективна, потому что использует рефекцию. IEquatable.Equals может предложить гораздо лучшую производительность и может быть реализован так, чтобы он не вызывал бокс.

public struct Int32 : IEquatable<Int32> {
    public bool Equals(Int32 other){ ... }
}

Следуйте тем же правилам, что и для переопределения Object.Equals при реализации IEquatable.Equals. См. Раздел 8.7.1 для подробных рекомендаций по переопределению Object.Equals

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

Поправьте меня, если я не прав, но реализация упомянута выше

public struct Complex 
{
   double re, im;
   public override bool Equals(Object obj) 
   {
      return obj is Complex && this == (Complex)obj;
   }
   public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }
   public static bool operator ==(Complex x, Complex y) 
   {
      return x.re == y.re && x.im == y.im;
   }
   public static bool operator !=(Complex x, Complex y) 
   {
      return !(x == y);
   }
}

Имеет большой недостаток. Я ссылаюсь на

  public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }

XORing является симметричным, поэтому Complex(2,1) и Complex(1,2) дают одинаковый хэш-код.

Вероятно, мы должны сделать что-то вроде:

  public override int GetHashCode() 
   {
      return re.GetHashCode() * 17 ^ im.GetHashCode();
   }

Большую часть времени вы можете избежать реализации Equals и GetHashcode в структурах - потому что компилятор автоматически реализует типы значений с использованием побитового содержимого + отражения для ссылочных членов.

Посмотрите на этот пост: что лучше всего подходит для хранилища данных Struct/Classes?

Так что для простоты использования вы все равно можете реализовать == и!=.

Но большую часть времени вы можете избежать реализации Equals и GetHashcode.
Случай, когда вам нужно реализовать Equals и GetHashCode, относится к полю, которое вы не хотите принимать во внимание.
Например, поле, которое меняется с течением времени, например, возраст человека или мгновенная скорость автомобиля (идентичность объекта не должна меняться, если вы хотите найти его в словаре в том же месте)

С уважением, лучший код

Просто для полноты я бы тоже советовал перегружать Equals метод:

public bool Equals(Complex other) 
{
   return other.re == re && other.im == im;
}

это реальное улучшение скорости, так как нет входного аргумента входного аргумента Equals(Object obj) метод

Некоторые рекомендации по использованию типов значений:

  • сделать их неизменными
  • переопределить Equals (тот, который принимает объект в качестве аргумента);
  • перегрузка Равно для получения другого экземпляра того же типа значения (например, * Равно (Сложное другое));
  • операторы перегрузки == и!=;
  • переопределить GetHashCode

Это происходит из этого поста: http://theburningmonk.com/2015/07/beware-of-implicit-boxing-of-value-types/

Основное различие между ними состоит в том, что == оператор является статическим, т.е. соответствующий метод для вызова определяется во время компиляции, в то время как Equals Метод вызывается динамически в экземпляре.
Определение обоих, вероятно, лучше всего сделать, даже если это имеет меньшее значение в случае структур, поскольку структуры не могут быть расширены (структура не может наследоваться от другой).

Другие вопросы по тегам