«^»
8.2. Providing an Equals method

8.2.1. Just like Java

By default, both:

tDate.Equals(tToday)

and:

tDate==tToday

will provide reference semantics: they ask whether tDate and tToday point to the same object.

The default version of the Equals method is provided by a (virtual) method declared in the class object. In the class Date, we can override object's Equals by declaring:

public override bool Equals(object pObject)
{
    if ( pObject==null || GetType()!=pObject.GetType() )
    {
        return false:
    }
    Date tDate = (Date)pObject;
    return iYear ==tDate.iYear  &&
           iMonth==tDate.iMonth &&
           iDay  ==tDate.iDay;                        
}

This version of Equals provides value semantics: so:

tDate.Equals(tToday)

asks whether tDate and tToday point to two objects that have the same value.

So far, all of this is the same as what you would do in Java.

8.2.2. Overloading the == operator

However, in C#, you can also overload many of the operators. For example, Date could provide declarations for the == and != operators. So instead of the above declaration of Equals, we could provide:

public static bool operator ==(Date pLeft, Date pRight)
{
    return pLeft.iYear ==pRight.iYear  &&
           pLeft.iMonth==pRight.iMonth &&
           pLeft.iDay  ==pRight.iDay;                        
}
public static bool operator !=(Date pLeft, Date pRight)
{
    return ! (pLeft==pRight);
}
public override bool Equals(object pObject)
{
    if ( pObject==null || GetType()!=pObject.GetType() )
    {
        return false:
    }
    Date tDate = (Date)pObject;
    return this==tDate;
}

Note: if you do provide a declaration for ==, you must also provide one for !=. Here are some statements showing a use of each of the above three declarations:

bool tTest1 = tFirstDate==tSecondDate;
bool tTest2 = tFirstDate!=tSecondDate;
bool tTest3 = tFirstDate.Equals(tSecondDate);

Now Equals and == are both providing value semantics.

As a use of == looks nicer than a call of Equals, you may be tempted not to bother with declaring Equals. However, this may be unwise. Albahari et al ([1], p46) say that declaring Equals ‘provides compatibility with other .NET languages that don't overload operators’. What this means is as follows: when using such a language, you will always be able to call the Equals method of your C# component even if your language does not allow you to use the == operator of your component.

8.2.3. == and Equals for value types

When the == operator is used with two values that are of some value type, the == operator will deliver true if the two values are bitwise equal. For most value types, this means that == has value semantics.

All value types are derived from System.ValueType. This type defines Equals to have the same meaning as the == operator.

8.2.4. Problems

There are a number of problems:

  1. The definition of == is left to the designer of a type: it could be providing value semantics or it might not be defined in which case you would get reference semantics for class types and bitwise equality for struct types. So you need to search for the class/struct declaration to find out the answer. Similarly for the Equals method. In Java, there is not so much uncertainty: when used with two reference variables, the == operator always provides reference semantics and the recommendation of the designers of Java is that a class should provide an equals method which implements value semantics.
  2. The other problem is that the overloading of == does not apply to interface variables. If you want to use an interface declaration to form the contract for a new type, surely it would be desirable to allow operator declarations in an interface.