Extend the complex number class to test equality in C#

The example Make a complex number class that demonstrates overloaded operators in C# builds a simple Complex class that includes overloaded +, -, *, and / operators that let you combine Complex objects. For example, a program can execute the following code.

Complex A = new Complex(1, 2);
Complex B = new Complex(3, 4);
Complex C = A + B;

Two operators that it didn't define are == and !=. Those are a little different. Note that if you define one of these operators you must define them both. For a simple class such as this one, that's easy because A == B is true when A != B is false.

There are two notions of equality for objects. Reference equality means two references point to the same object. Value equality means two objects contain the same values. For example, if two Employee objects person1 and person2 refer to the same object, they have reference equality. If they refer to different objects that happen to have the same FirstName, LastName, and other properties, then they have value equality.

Note that two objects with reference equality always have value equality because they refer to the same object. The converse is not true.

With all of that in mind, Microsoft has several recommendations about equality.

  • Reference equality operators == and != are already defined by default so if that's what you want, you don't need to do anything else.
  • Microsoft recommends against implementing value equality operators for classes that are not invariant. In other words, if it is possible to change the values of the object's properties after the object is set, then you should stick with reference equality. (I'm not completely sure why they make this recommendation. Perhaps it's because two objects that are equal at one time may become not equal later.)

The Complex class we have been building is not invariant because you can set its Re and Im fields in your code so Microsoft would recommend against using value equality but I'm going to do it anyway because it makes sense for complex numbers. (In fact, Microsoft would probably recommend that the whole thing be converted into a structure instead of a class with Re and Im implemented as read-only properties.)

If you override the == and != operators, you should also override the inherited Equals and GetHashCode methods. If you don't override those methods, Visual Studio issues a warning. The Equals method should basically do whatever == should do. The GetHashCode method should return an int value that code can use for hashing.

Furthermore Microsoft makes these recommendations about the Equals method:

  • Identity: x.Equals(x) should always be true.
  • Commutative: x.Equals(y) should be the same as y.Equals(x).
  • Transitive: If x.Equals(y) is true and y.Equals(z) is true, then x.Equals(z) should return true.
  • Repeated calls to x.Equals(y) should all return the same value if x and y have not changed.
  • The value x.Equals(null) should be false for any (non-null) object x.
  • Equals should not throw exceptions.
  • In addition to overriding Equals(object), you should provide Equals(type) for ths class's type.

The following code shows how this example defines the Complex class's Equals methods.

// Return true if the objects contain the same values.
public bool Equals(Complex B)
{
if ((object)B == null) return false;
return ((this.Re == B.Re) && (this.Im == B.Im));
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (!(obj is Complex)) return false;
return this.Equals(obj as Complex);
}

The first method takes a Complex as a parameter. if that parameter is null, the method returns false. Notice that the code converts the parameter to a generic object before using == so it doesn't try to invoke the Complex class's version of ==, which is defined shortly.

The second version of Equals takes an object as a parameter, converts it into a Complex, and calls the first version.

The following code shows how the Complex class overrides the == operator.

// Return A == B.
public static bool operator ==(Complex A, Complex B)
{
// If both are null or the same object, return true.
if (System.Object.ReferenceEquals(A, B)) return true;

// If one is null but not the other, return false.
if (((object)A == null) || ((object)B == null)) return false;

// Compare the field values.
return A.Equals(B);
}

The == operator uses the ReferenceEquals method to see if A and B refer to the same object (or they both refer to null). If they do, then their values are equal.

If either A or B is null, they are not equal.

Finally the method calls Equals to see if the objects have the same values.

The following code shows how the Complex class overrides the != operator.

// Return A != B.
public static bool operator !=(Complex A, Complex B)
{
return !(A == B);
}

This method simply invokes == and negates the result.

The final piece in this example is the implementation of GetHashCode.

// Return a hash code for this object.
public override int GetHashCode()
{
return (Re.GetHashCode() + Im).GetHashCode();
}

This code gets the hash code for the complex number's real part, adds the imaginary part, and then gets the hash code of the result. Exactly what value this has isn't too important as long it's fairly "random" so different "typical" values are unlikely to have the same hash code. In particular, x + yi has a different hash code than y + xi.

(Yes, equality testing is a bit confusing!)

   

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments

  • 11/12/2013 9:37 AM Geoff Hirst wrote:
    Rod,
    For some time it has been recommended that if you override equals, you should also override GetHashCode. From what I have read on this prime numbers are a kind of good place to start. Do you have a recommended way to do this and do you have anything that can determine the equality of List?
    Reply to this
    1. 11/13/2013 9:43 AM Rod Stephens wrote:
      Hi Geoff,

      Microsoft tends to make recommendations without saying why. I think this one is because if two objects are considered "equal" they should produce the same hash code.

      Imagine what happens if you store an item with key K1 in a hash table. If you try to look up an item by using an "equal" key K2, the two keys have to hash to the same value or the hash table won't be able to find the object you want.

      I think the method you use isn't too important as long as it's a reasonable hash code (i.e. doesn't map every value to 7) and two "equal" objects have the same hash code.

      Sometimes you can hash the pieces of an object and then combine them with ^ or + or something. In the case of complex numbers, I didn't want to do that because then A + Bi would have the same hash code as B + Ai. There's still a chance that two values will have the same hash code (there always is), but at least such an obvious case is avoided.

      Let me know if that doesn't make sense.
      Reply to this
  • 11/13/2013 9:47 AM Rod Stephens wrote:
    As for the equality of lists, you need to decide what you mean by equality. Reference equality means they point to the same list. That's easy but probably not what you want.

    You can loop through the lists to make sure all of their items refer to the same objects, but even that may not be what you want.

    For example, suppose you have two lists of Person objects. Both contain an object with name Geoff Hirst but they're two separate objects. A reference equality test says the lists are different because they don't contain the same instances of the Person class. But really you may want to say they are the same because they contain objects that represent the same people.

    And finally there's the issue of order. If two lists contain the same objects in different orders, are they the same?

    Here's my preference: If the items in the lists implement IComparable, then you can sort the lists and loop through them comparing them to see if the lists hold objects with the same values. (In fact, that might make a good post!)

    Let me know if that doesn't make sense.
    Reply to this
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.