C# Programming Guide
Guidelines for Overriding Equals() and Operator == (C# Programming Guide)

In C#, there are two different kinds of equality: reference equality (also known as identity) and value equality. Value equality is the generally understood meaning of equality: it means that two objects contain the same values. For example, two integers with the value of 2 have value equality. Reference equality means that there are not two objects to compare. Instead, there are two object references and both of them refer to the same object. This can occur through simple assignment, as shown in the following example:

C#
object a = new object();
object b = a;
bool areEqual = System.Object.ReferenceEquals(a, b);  //returns true
System.Console.WriteLine("a and b {0} reference equality", areEqual ? "have" : "do not have");

object c = b;
areEqual = System.Object.ReferenceEquals(a, c);  //if a == b and b == c, then a == c
System.Console.WriteLine("a and c {0} reference equality", areEqual ? "have" : "do not have");

// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();        

In this code, only one object exists, but there are multiple references to that object: a and b. Because they both refer to the same object, they have reference equality. If two objects have reference equality, they also have value equality, but value equality does not guarantee reference equality.

To check for reference equality, use ReferenceEquals. To check for value equality, you should generally use Equals. However, Equals as it is implemented by Object just performs a reference identity check. It is therefore important, when you call Equals, to verify whether the type overrides it to provide value equality semantics. When you create your own types, you should override Equals.

Because Equals is a virtual method, any class can override its implementation. Any class that represents a value, essentially any value type, or a set of values as a group, such as a complex number class, should override Equals. If the type implements IComparable, it should override Equals.

The new implementation of Equals should follow all the guarantees of Equals:

  • x.Equals(x) returns true.

  • x. Equals (y) returns the same value as y. Equals (x).

  • if (x. Equals (y) && y. Equals (z)) returns true, then x. Equals (z) returns true.

  • Successive invocations of x. Equals (y) return the same value as long as the objects referenced by x and y are not modified.

  • x. Equals (null) returns false (for non-nullable value types only. For more information, see Nullable Types (C# Programming Guide).)

The new implementation of Equals should not throw exceptions. It is recommended that any class that overrides Equals also override Object..::.GetHashCode. It is also recommended that in addition to implementing Equals (object), any class also implement Equals (type) for their own type, to enhance performance. For example:

C#
class TwoDPoint : System.Object
{
    public readonly int x, y;

    public TwoDPoint(int x, int y)  //constructor
    {
        this.x = x;
        this.y = y;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        TwoDPoint p = obj as TwoDPoint;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public bool Equals(TwoDPoint p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

Any derived class that can call Equals on the base class should do so before finishing its comparison. In the following example, Equals calls the base class Equals, which checks for a null parameter and compares the type of the parameter with the type of the derived class. That leaves the implementation of Equals on the derived class the task of checking the new data field declared on the derived class:

C#
class ThreeDPoint : TwoDPoint
{
    public readonly int z;

    public ThreeDPoint(int x, int y, int z)
        : base(x, y)
    {
        this.z = z;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter cannot be cast to ThreeDPoint return false:
        ThreeDPoint p = obj as ThreeDPoint;
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return base.Equals(obj) && z == p.z;
    }

    public bool Equals(ThreeDPoint p)
    {
        // Return true if the fields match:
        return base.Equals((TwoDPoint)p) && z == p.z;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode() ^ z;
    }
}

By default, the operator == tests for reference equality by determining whether two references indicate the same object. Therefore, reference types do not have to implement operator == in order to gain this functionality. When a type is immutable, that is, the data that is contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. It is not a good idea to override operator == in non-immutable types.

Overloaded operator == implementations should not throw exceptions. Any type that overloads operator == should also overload operator !=. For example:

C#
//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)a == null) || ((object)b == null))
    {
        return false;
    }

    // Return true if the fields match:
    return a.x == b.x && a.y == b.y && a.z == b.z;
}

public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
    return !(a == b);
}
ms173147.alert_note(en-us,VS.90).gifNote:

A common error in overloads of operator == is to use (a == b), (a == null), or (b == null) to check for reference equality. This instead creates a call to the overloaded operator ==, causing an infinite loop. Use ReferenceEquals or cast the type to Object, to avoid the loop.

See Also

Concepts

Reference

Change History

Date

History

Reason

August 2008

Added information to third paragraph.

Information enhancement.

Tags :


Community Content

vcomrhencke
Example code is not correct.

It should be noted that Microsoft's code here doesn't follow its own guidelines. Specifically, their implementation of override bool Equals(object obj) does not guarantee that x.Equals(y) returns the same value as y.Equals(x).

For example:

object x = new TwoDPoint(1, 2);
object y = new ThreeDPoint(1, 2, 3);
y.Equals(x); // Returns false
x.Equals(y); // Returns true(!)

There are other issues here, but for those looking for a guide on writing equality/hashcode operations correctly, take the above code with a dose of caution.


Jarlac
RE: Example code is not correct.

It get worse if the two variables are declared by their types instead of using of object

var x = new TwoDPoint(1, 2);
var y = new ThreeDPoint(1, 2, 3);
y.Equals(x); // Returns true //"Should" return false
x.Equals(y); // Returns true //"Should" return false

It is important to notice however that in this case it does follow the guideline described above, but the result is incorrect*

* In fact it could also be argued that the answer is neither true nor false but instead it is undetermined and should throw an Exception. However as stated in the Guidelines: "The new implementation of Equals should not throw exceptions", then when comparing two objects of different types even when one is a derived class from the other should always return false.

Think of this as comparing the terms Vehicle and Car. A car is a kind of vehicle but it is not correct to say the a vehicle is a synonym of car

Tags : null

Page view tracker