Reputation: 331082
Say for a Point2 class, and the following Equals:
public override bool Equals ( object obj )
public bool Equals ( Point2 obj )
This is the one that is shown in the Effective 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 (e.g., 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 ) ) );
}
Upvotes: 50
Views: 75354
Reputation: 655
I've found this to work for me - I let GetHashCode() to do all the heavy lifting.
After that, any two objects are equal if and only if their hash codes are equal.
public override int GetHashCode() => <-- IMPLEMENT THIS -->
public virtual bool Equals(T other) => other?.GetHashCode() == GetHashCode();
public override bool Equals(object other) => Equals(other as T);
public static bool operator ==(T item1, T item2) => item1?.GetHashCode() == item2?.GetHashCode();
public static bool operator !=(T item1, Titem2) => !(item1 == item2);
Example of a Wrapper class which adds some info to a sealed class of type S.
public class Wrapper<S>
{
public S obj;
// Handle obj being null
public override int GetHashCode() => obj?.GetHashCode() ?? 0;
// The rest are the same as above
public virtual bool Equals(Wrapper<S> other) => other?.GetHashCode() == GetHashCode();
public override bool Equals(object other) => Equals(other as Wrapper<S>);
public static bool operator ==(Wrapper<S> item1, Wrapper<S> item2) => item1?.GetHashCode() == item2?.GetHashCode();
public static bool operator !=(Wrapper<S> item1, Wrapper<S> item2) => !(item1 == item2);
}
Upvotes: 1
Reputation: 51
Using C# 7 and the is type varname
pattern matching syntax provides for a clean Equals(object)
that deals with null
and type checking using either of the below approaches:
// using strongly-typed overload of Equals
public override bool Equals(object obj) => (obj is Point2 other) && Equals(other);
public bool Equals(Point2 other);
// using the == operator (requires != to also be defined)
public override bool Equals(object obj) => (obj is Point2 other) && this == other;
public static bool operator ==(Point2 lhs, Point2 rhs);
public static bool operator !=(Point2 lhs, Point2 rhs);
(Obviously you need to provide implementation of the relevant method stubs.)
Upvotes: 5
Reputation: 273274
There is a whole set of guidelines on MSDN as well. You should read them well, it is both tricky and important.
A few points I found most helpful:
Value Types don't have Identity, so in a struct Point
you will usually do a member by member compare.
Reference Types usually do have identity, and therefore the Equals test usually stops at ReferenceEquals (the default, no need to override). But there are exceptions, like string and your class Point2
, where an object has no useful identity and then you override the Equality members to provide your own semantics. In that situation, follow the guidelines to get through the null and other-type cases first.
And there are good reasons to keep GethashCode()
and operator==
in sync as well.
Upvotes: 36
Reputation: 166
The simple and best way to override Equals looks like:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public override bool Equals(object other)
{
Person otherItem = other as Person;
if (otherItem == null)
return false;
return Age == otherItem.Age && Name == otherItem.Name;
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + Age.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
return hash;
}
}
Override the GetHashCode method to allow a type to work correctly in a hash table.
Upvotes: 2
Reputation: 1251
There is also a Fody plugin Equals.Fody that generates Equals() and GetHashCode() automatically
Upvotes: 2
Reputation: 393
Slight variants of forms already posted by several others...
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 );
}
Trying to find a way to implement operator == using Equals to avoid duplicating the value comparison logic... without any redundant tests (ReferenceEquals calls w/ the same parameters) or unnecessary tests (this can't be null in the instance.Equals method) and without any explicit conditionals ("ifs"). More of a mind teaser than anything useful.
Closest I can think of is this, but it feels like it should be possible without an extra method :)
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;
}
Remembering how tedious and error prone this all is (I'm almost sure there's an error in the above code... which still sucks because who wants to subclass a Type just to make equality checks slightly simpler?), going forward I think I'll just create some static methods that handle all the null checks and accept a delegate or require and interface to perform the comparison of values (the only part that really changes Type to Type).
It'd be great if we could just add attributes onto the fields/properties/methods that need to be compared and let the compiler/runtime handle all the tedium.
Also make sure GetHashCode() values are equal for any instances in which which .Equals(object) returns true or crazy shit can happen.
Upvotes: 0
Reputation: 531
The technique I've used that has worked for me is as follows. Note, I'm only comparing based on a single property (Id) rather that two values. Adjust as needed
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);
}
}
}
Upvotes: 15
Reputation: 51081
In the one that takes an obj, if the type of obj is Point2, call the type specific Equals. Inside the type specific Equals, make sure that all the members have the same value.
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.
}
You probably ought to override GetHashCode as well to make sure that objects that are "equal" have the same hash code.
Upvotes: 47
Reputation: 7595
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 )
Upvotes: 0
Reputation: 41630
Lie Daniel L said,
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) {
...
}
Upvotes: 0
Reputation: 109025
Likely you want to both override Equals(object) and define Equals(MyType) because the latter avoids boxing. And override the equality operator.
.NET Framework Guidelines book (2nd ed) has more coverage.
Upvotes: 2