Make a complex number class with equality in C#

complex number class with equality

The example Make a complex number class with 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. That means you can define one and then use its negation to define the other.

There are two notions of equality for objects: reference equality and value equality. 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 reverse is not true.

With all 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’ve 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. 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 == does. The GetHashCode method should return an int value that a program 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 also be 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 the class’s type.

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

// Return A == B.
public static bool operator ==(Complex A, Complex B)
{
    if (object.ReferenceEquals(A, null))
        return object.ReferenceEquals(B, null);
    if (object.ReferenceEquals(B, null)) return false;

    // Compare the field values.
    return ((A.Re == B.Re) && (A.Im == B.Im));
}

This operator uses the object.ReferenceEquals method to test reference equality between A and null. If A is null, then B must be also to be equal.

If A and B are not null, then the code compares their Re and Im values.

The other equality methods follow easily from ==.

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

// Return true if the objects contain the same values.
public bool Equals(Complex B)
{
    return this == B;
}
public override bool Equals(object obj)
{
    return Equals(obj as Complex);
}

The != operator calls == and negates the result.

The Equals method simply calls ==.

The second Equals method uses the as keyword to convert an object into a Complex or null if the object cannot be converted into a Complex. It then passes the resulting value into the first Equals method.

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 returns the exclusive OR of the hash codes of the value’s real and imaginary parts. 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.

Hashing classes such as Dictionary use the hash code as a quick test to see if two objects might be equal. If you add a new key to a Dictionary, it compares the hash code for the new item to those that are already in the hash table. If there is a match, the Dictionary uses the Equals method to see if the values are actually equal.

Yes, equality testing is confusing! Stay tuned for more on complex numbers.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in classes, mathematics, OOP, operators and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

3 Responses to Make a complex number class with equality in C#

  1. Geoff Hirst says:

    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?

  2. Rod Stephens says:

    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.

  3. Rod Stephens says:

    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.

Leave a Reply

Your email address will not be published. Required fields are marked *