6

Implementing Equals() for reference types is harder than it seems. My current canonical implementation goes like this:

public bool Equals( MyClass obj )
{
  // If both refer to the same reference they are equal.
  if( ReferenceEquals( obj, this ) )
    return true;

  // If the other object is null they are not equal because in C# this cannot be null.
  if( ReferenceEquals( obj, null ) )
   return false;

   // Compare data to evaluate equality    
   return _data.Equals( obj._data );
}

public override bool Equals( object obj )
{
  // If both refer to the same reference they are equal.
  if( ReferenceEquals( obj, this ) )
    return true;

  // If the other object is null or is of a different types the objects are not equal. 
  if( ReferenceEquals( obj, null ) || obj.GetType() != GetType() )
    return false;

  // Use type-safe equality comparison
  return Equals( (MyClass)obj );
}

public override int GetHashCode()
{
  // Use data's hash code as our hashcode  
  return _data.GetHashCode();
}

I think that this covers all corner (inheritance and such) cases but I may be wrong. What do you guys think?

4

6 回答 6

4

I wrote a fairly comprehensive guide to this a while back. For a start your equals implementations should be shared (i.e. the overload taking an object should pass through to the one taking a strongly typed object). Additionally you need to consider things such as your object should be immutable because of the need to override GetHashCode. More info here:

http://gregbeech.com/blog/implementing-object-equality-in-dotnet

于 2008-09-16T17:27:11.207 回答
1

Better hope that this._data is not null if it's also a reference type.

public bool Equals( MyClass obj )
{
    if (obj == null) {
        return false;
    }
    else {
        return (this._data != null && this._data.Equals( obj._data ))
                         || obj._data == null;
    }
}

public override bool Equals( object obj )
{
    if (obj == null || !(obj is MyClass)) {
        return false;
    }
    else {
        return this.Equals( (MyClass)obj );
    }
}

public override int GetHashCode() {
    return this._data == null ? 0 : this._data.GetHashCode();
}
于 2008-09-16T17:22:54.757 回答
0

It depends on whether you're writing a value type or a reference type. For a sortable value type, I recommend this: A code snippet for Visual Studio 2005 that implements a skeleton value type adhering to Framework Design Guidelines

于 2008-09-16T16:59:02.410 回答
0

Concerning inheritance, I think you should just let the OO paradigm does its magic.

Specifically, the GetType() check should be removed, it might break polymorphism down the line.

于 2008-09-16T17:01:02.297 回答
0

I agree with chakrit, objects of different types should be allowed to be semantically equal if they have the same data or ID.

Personally, I use the following:

    public override bool Equals(object obj)
    {
        var other = obj as MyClass;
        if (other == null) return false;

        return this.data.Equals(other.data);
    }
于 2008-09-16T17:17:09.747 回答
0

As the link in the accepted answer by Greg Beech is broken, I hope this answer might be helpful to some.

Microsoft's documentation provides the following example for a typical implementation of Equals() on reference types (i.e. class):

public override bool Equals(object obj) => this.Equals(obj as TwoDPoint);

public bool Equals(TwoDPoint p)
{
   if (p is null)
   {
         return false;
   }

   // Optimization for a common success case.
   if (Object.ReferenceEquals(this, p))
   {
         return true;
   }

   // If run-time types are not exactly the same, return false.
   if (this.GetType() != p.GetType())
   {
         return false;
   }

   // Return true if the fields match.
   // Note that the base class is not invoked because it is
   // System.Object, which defines Equals as reference equality.
   return (X == p.X) && (Y == p.Y);
}

The complete example with more details including what to do in derived classes or for structs, can be found at "How to define value equality for a class or struct (C# Programming Guide)"

As an alternative to what is described in Microsoft's doc, the method GetHashCode() can also be implemented as follows:

public override int GetHashCode()
{
   return HashCode.Combine(X, Y);
}
于 2021-10-09T03:50:30.390 回答