Как наилучшим образом реализовать Equals для пользовательских типов?

Скажем, для classа Point2 и следующих Равновений:

public override bool Equals ( object obj ) public bool Equals ( Point2 obj ) 

Это тот, который показан в Эффективном C # 3:

 public override bool Equals ( object obj ) { // STEP 1: Check for null if ( obj == null ) { return false; } // STEP 3: equivalent data types if ( this.GetType ( ) != obj.GetType ( ) ) { return false; } return Equals ( ( Point2 ) obj ); } public bool Equals ( Point2 obj ) { // STEP 1: Check for null if nullable (eg, a reference type) if ( obj == null ) { return false; } // STEP 2: Check for ReferenceEquals if this is a reference type if ( ReferenceEquals ( this, obj ) ) { return true; } // STEP 4: Possibly check for equivalent hash codes if ( this.GetHashCode ( ) != obj.GetHashCode ( ) ) { return false; } // STEP 5: Check base.Equals if base overrides Equals() System.Diagnostics.Debug.Assert ( base.GetType ( ) != typeof ( object ) ); if ( !base.Equals ( obj ) ) { return false; } // STEP 6: Compare identifying fields for equality. return ( ( this.X.Equals ( obj.X ) ) && ( this.Y.Equals ( obj.Y ) ) ); } 

8 Solutions collect form web for “Как наилучшим образом реализовать Equals для пользовательских типов?”

Существует целый набор рекомендаций по MSDN . Вы должны хорошо их прочесть, это сложно и важно.

Несколько моментов, которые я нашел наиболее полезными:

  • Типы значений не имеют Identity, поэтому в struct Point вы обычно делаете член с помощью сравнения элементов.

  • У ссылочных типов обычно есть личность, поэтому тест Equals обычно останавливается на ReferenceEquals (по умолчанию, не нужно переопределять). Но есть исключения, такие как строка и ваш class Point2 , где объект не имеет полезной идентификации, а затем вы переопределяете членов Equality для предоставления своей собственной семантики. В этой ситуации следуйте рекомендациям, чтобы сначала пройти нулевые и другие случаи.

  • И есть веские причины держать GethashCode() и operator== в синхронизации.

В той, которая принимает obj, если тип obj является Point2, вызовите тип Equals. Внутри определенного типа Equals убедитесь, что все члены имеют одинаковое значение.

 public override bool Equals ( object obj ) { return Equals(obj as Point2); } public bool Equals ( Point2 obj ) { return obj != null && obj.X == this.X && obj.Y == this.Y ... // Or whatever you think qualifies as the objects being equal. } 

Вероятно, вы должны переопределить GetHashCode, чтобы убедиться, что объекты, «равные», имеют один и тот же хеш-код.

Техника, которую я использовал, которая работала для меня, заключается в следующем. Заметьте, я сравниваю только одно свойство (Id), а не два значения. При необходимости отрегулируйте

 using System; namespace MyNameSpace { public class DomainEntity { public virtual int Id { get; set; } public override bool Equals(object other) { return Equals(other as DomainEntity); } public virtual bool Equals(DomainEntity other) { if (other == null) { return false; } if (object.ReferenceEquals(this, other)) { return true; } return this.Id == other.Id; } public override int GetHashCode() { return this.Id; } public static bool operator ==(DomainEntity item1, DomainEntity item2) { if (object.ReferenceEquals(item1, item2)) { return true; } if ((object)item1 == null || (object)item2 == null) { return false; } return item1.Id == item2.Id; } public static bool operator !=(DomainEntity item1, DomainEntity item2) { return !(item1 == item2); } } } 
  • Определите, что означает идентификатор. Если ссылочная идентификация будет использоваться по умолчанию, то унаследованные равны будут работать.
  • Если тип значения (и, следовательно, значение идентификатора) вам нужно определить.
  • Если тип classа, но имеет семантику значения, то определите.

Вероятно, вы хотите как переопределить Equals (объект), так и определить Equals (MyType), потому что последний избегает бокса. И переопределить оператор равенства.

В книге рекомендаций .NET Framework (2-е изд.) Больше внимания.

Ли Дэниел Л сказал:

 public override bool Equals(object obj) { Point2 point = obj as Point2; // Point2? if Point2 is a struct return point != null && this.Equals(point); } public bool Equals(Point2 point) { ... } 

Небольшие варианты форм, уже опубликованные несколькими другими …

 using System; ... public override bool Equals ( object obj ) { return Equals(obj as SomeClass); } public bool Equals ( SomeClass someInstance ) { return Object.ReferenceEquals( this, someInstance ) || ( !Object.ReferenceEquals( someInstance, null ) && this.Value == someInstance.Value ); } public static bool operator ==( SomeClass lhs, SomeClass rhs ) { if( Object.ReferenceEquals( lhs, null ) ) { return Object.ReferenceEquals( rhs, null ); } return lhs.Equals( rhs ); //OR return Object.ReferenceEquals( lhs, rhs ) || ( !Object.ReferenceEquals( lhs, null ) && !Object.ReferenceEquals( rhs, null ) && lhs.Value == rhs.Value ); } public static bool operator !=( SomeClass lhs, SomeClass rhs ) { return !( lhs == rhs ); // OR return ( Object.ReferenceEquals( lhs, null ) || !lhs.Equals( rhs ) ) && !Object.ReferenceEquals( lhs, rhs ); } 

Попытка найти способ реализовать operator == с помощью Equals, чтобы избежать дублирования логики сравнения значений … без каких-либо избыточных тестов (вызовы ReferenceEquals с теми же параметрами) или ненужных тестов (это не может быть нулевым в экземпляре.Equals метод) и без каких-либо явных условностей («ifs»). Больше ума тизер, чем что-нибудь полезное.

Ближе всего я могу подумать, но это похоже на то, что это возможно без дополнительного метода 🙂

 public bool Equals ( SomeClass someInstance ) { return Object.ReferenceEquals( this, someInstance ) || (!Object.ReferenceEquals( someInstance, null ) && EqualsNonNullInstance( someInstance ); } public static bool operator ==( SomeClass lhs, SomeClass rhs ) { return Object.ReferenceEquals( lhs, rhs ) || ( !Object.ReferenceEquals( lhs, null ) && !Object.ReferenceEquals( rhs, null ) && lhs.EqualsNonNullInstance( rhs ) ); } //super fragile method which returns logical non-sense protected virtual bool EqualsNonNullInstance ( SomeClass someInstance ) { //In practice this would be a more complex method... return this.Value == someInstance.Value; } 

Помня о том, насколько утомительным и подверженным ошибкам все это (я почти уверен, что в приведенном выше коде есть ошибка … которая все еще сосет, потому что кто хочет подclassифицировать Type просто для того, чтобы сделать проверки равенства немного проще?), В будущем я думаю, что я ‘просто создайте некоторые статические методы, которые обрабатывают все нулевые проверки и принимают делегат или требуют и интерфейс для выполнения сравнения значений (единственная часть, которая действительно изменяет Type to Type).

Было бы здорово, если бы мы просто добавили атрибуты в поля / свойства / методы, которые нужно сравнить, и пусть компилятор / среда выполнения обработает всю скуку.

Также убедитесь, что значения GetHashCode () равны для всех экземпляров, в которых может быть возвращено истинное или безумное дерьмо.

Существует также плагин Fody Equals.Fody, который автоматически генерирует Equals () и GetHashCode ()

 public override bool Equals ( object obj ) { // struct return obj is Point2 && Equals ( ( Point2 ) value ); // class //return Equals ( obj as Point2 ); } public bool Equals ( Point2 obj ) 
  • Разница между окончательным и эффективным окончательным
  • java: что это: [Ljava.lang.Object ;?
  • Синтетический class в Java
  • Создание эффекта масштабирования изображения при наведении с помощью CSS?
  • Что означает «Не удалось найти или загрузить основной class»?
  • C ++ 11 допускает инициализацию в classе нестатических и неконстантных элементов. Что изменилось?
  • Каковы различия между структурой и classом в C ++?
  • Определение "POCO"
  • Что такое public, private и protected в объектно-ориентированном программировании?
  • Создание массива объектов в Java
  • включение текущего classа в качестве annotations возвращаемого типа
  • Давайте будем гением компьютера.