X=Y, then X's behavior should not change in any value context.
If your comparison compares only one aspect of two objects, name the method accordingly--do not make it an equality operator. (TComparators aren't subject to this restriction, because they explicitly compare only some aspect of the objects.) For example, if your equality operator only ensures that two objects have the same area, you should name it HasSameArea(), not ==. If it only ensures that two objects have the same name, it should be HasSameName().
Ensure that your Hash method is coordinated with your equality. The invariant is: if X==Y, then Hash(X) = Hash(Y). For information on Hash methods, see "Portable hash" on page 113.
Watch for subclasses--in the majority of cases two objects of different classes are not equal. Because of polymorphism, you must check the types of classes to get this right. Because runtime type information (RTTI) isn't currently supported, you will generally do this with an MCollectible check (see the "Equality sample" in the next section).
The equality semantics of surrogate classes should depend on whether they act like pointers or act like values. If they act like pointers, they should compare equal if they refer to the same object. If they act like values, they should compare equal if the objects they refer to compare equal.