#419 – Override Relational Operators When You Implement IComparable

When a class implements IComparable, it must implement the CompareTo method.  For completeness, you should also override the relational operators.

Here’s an example.

    public class Rectangle : IEquatable<Rectangle>, IComparable<Rectangle>
    {
        public int Height { get; set; }
        public int Width { get; set; }

        public Rectangle(int height, int width)
        {
            Height = height;
            Width = width;
        }

        public override bool Equals(object obj)
        {
            return this.Equals(obj as Rectangle);
        }

        public override int GetHashCode()
        {
            return Height.GetHashCode() ^ Width.GetHashCode();
        }

        public bool Equals(Rectangle r)
        {
            if (ReferenceEquals(r,null))
                return false;

            return ((Height == r.Height) && (Width == r.Width) ||
                    (Height == r.Width) && (Width == r.Height));
        }

        public static bool operator ==(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
            {
                return ReferenceEquals(r2, null) ? true : false;
            }

            return r1.Equals(r2);
        }

        public static bool operator !=(Rectangle r1, Rectangle r2)
        {
            return !(r1 == r2);
        }

        // Result:
        //  < 0 : this instance less than r
        //  = 0 : this instance equivalent to r
        //  > 0 : this instance greater than r
        public int CompareTo(Rectangle r)
        {
            if (ReferenceEquals(r, null))
                return 1;  

            if (this.Equals(r))
                return 0;

            else if (this.Area() == r.Area())
                return this.Width - r.Width;

            else
                return this.Area() - r.Area();
        }

        public static bool operator <(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
                return false;

            else
                return (r1.CompareTo(r2) < 0) ? true : false;
        }

        public static bool operator >(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
                return false;

            else
                return (r1.CompareTo(r2) > 0) ? true : false;
        }

        public static bool operator <=(Rectangle r1, Rectangle r2)
        {
            return (r1 < r2) || (r1 == r2);
        }

        public static bool operator >=(Rectangle r1, Rectangle r2)
        {
            return (r1 > r2) || (r1 == r2);
        }

        public int Area()
        {
            return Height * Width;
        }
    }

#416 – Use an Epsilon to Compare Two Floating Point Numbers

Because of problems with the precision of floating point numbers, two numbers that would ought to be considered equal might actually have different values.  Because of this, you typically don’t want to use the == operator on floating point numbers, when checking for equality.  Instead, you likely want to see if the difference between the two numbers is small enough that the numbers can be considered equal.

Here’s helper function that determines if two float values are nearly equal.

        public static bool NearlyEqual(float f1, float f2)
        {
            // Equal if they are within 0.00001 of each other
            return Math.Abs(f1 - f2) < 0.00001;
        }

We then get the following results:

            float f1 = 1.0f / 6.0f;
            float f2 = (f1 * 6.0f) - 1.0f;   // Should be 0.0
            float f3 = 0.0f;

            bool check = (f2 == f3);       // false !
            check = NearlyEqual(f2, f3);   // true

The value that you choose for epsilon will typically depend on your application.

#415 – Be Careful When Checking Floating Point Numbers for Equality

When you check two floating point numbers for equality in C#, you might sometimes be surprised at the result.

Consider the following example.  f2 is equal to six sixths (1.0), minus 1.0–which should be equal to 0.0.  But when we compare the result to 0.0, we see that the values are not equal.

            float f1 = 1.0f / 6.0f;
            float f2 = (f1 * 6.0f) - 1.0f;   // Should be 0.0
            float f3 = 0.0f;

            bool check = (f2 == f3);    // Should be true, but is false !

This happens because floating point numbers can’t necessarily be represented exactly when stored in a floating point variable.  Instead, the value stored can be very close, but not equal to, the desired value.

Here’s the output from the example above.  f2 should be equal to 0.0, after our calculation.  It is instead very nearly equal to 0.0.  Because of this, the comparison to the constant 0.0 fails.

#414 – Equivalence Can Be Based on a Subset of Fields

When you define value equality for a type, you typically compare all fields in the two instances to determine whether they are equivalent.  You can also use just a subset of the fields in the comparison.

Below, two Programmer instances are equivalent if their Name and Age properties match.  But their Mood properties are not used in the comparison.

    public class Programmer
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Mood { get; set; }

        // constructor omitted

        public static bool operator ==(Programmer p1, Programmer p2)
        {
            return (p1.Name == p2.Name) && (p1.Age == p2.Age);
        }

        public static bool operator !=(Programmer p1, Programmer p2)
        {
            return !(p1 == p2);
        }
    }

Test results:

            Programmer p1 = new Programmer("Sean", 47, "Elated");
            Programmer p2 = new Programmer("Sean", 47, "Surly");
            Programmer p3 = new Programmer("Bob", 47, "Surly");

            bool check = (p1 == p2);        // true
            check = (p2 == p3);             // false

#412 – Guidelines when Overloading == Operator for a Value Type

When you overload the == operator for a value type, you should also:

  • Overload != operator
  • Override Equals method
  • Overload < and > operators

This is also true for reference types.

#410 – Overloading the == Operator for a Value Type

A user-defined struct automatically inherits an Equals method that performs a value equality check by comparing each field of the struct. The == operator, however, is not automatically defined.  If you want to use the == operator for instances of a struct, you need to overload the == operator.

    public struct PersonHeight
    {
        public int Feet { get; set; }
        public int Inches { get; set; }

        public PersonHeight(int feet, int inches) : this()
        {
            Feet = feet;
            Inches = inches;
        }

        public static bool operator ==(PersonHeight ph1, PersonHeight ph2)
        {
            return (ph1.Feet == ph2.Feet) && (ph1.Inches == ph2.Inches);
        }

        public static bool operator !=(PersonHeight ph1, PersonHeight ph2)
        {
            return !(ph1 == ph2);
        }
    }

Some test cases:

            PersonHeight ph1 = new PersonHeight(5, 10);
            PersonHeight ph2 = new PersonHeight(5, 10);

            // Returns true, default Equals method compares each field
            bool check = ph1.Equals(ph2);

            // == operator also now works - true
            check = (ph1 == ph2);

#409 – Example of Overloading the == Operator

Here’s a full example that shows how to overload the == operator for a reference type.

    public class PersonHeight
    {
        public int Feet { get; set; }
        public int Inches { get; set; }

        // Constructor goes here

        public override bool Equals(object obj)
        {
            return ((obj is PersonHeight) && ((PersonHeight)obj == this));
        }

        public override int GetHashCode()
        {
            return Feet.GetHashCode() ^ Inches.GetHashCode();
        }

        public static bool operator ==(PersonHeight ph1, PersonHeight ph2)
        {
            if (ReferenceEquals(ph1, ph2))
                return true;

            if (((object)ph1 == null) || ((object)ph2 == null))
                return false;

            return (ph1.Feet == ph2.Feet) && (ph1.Inches == ph2.Inches);
        }

        public static bool operator !=(PersonHeight ph1, PersonHeight ph2)
        {
            return !(ph1 == ph2);
        }

        // Also overload < and > operators
    }

Test cases:

            PersonHeight ph1 = new PersonHeight(5, 10);
            PersonHeight ph2 = new PersonHeight(5, 10);
            PersonHeight ph3 = null;

            bool check = ph1.Equals(null);
            check = ph1.Equals("NO!");
            check = ph1.Equals(ph2);
            check = PersonHeight.Equals(ph1, ph2);

            check = (ph1 == null);
            check = (ph3 == null);
            check = (ph1 == ph2);

#408 – Overloading the == Operator for a Reference Type

You can override the Equals method for a type to implement value equality for the type. You can also overload the == operator.

By default, the == operator will do a reference equality check, only returning true if two references point to exactly the same object.  This is normally what you want for a reference type, using the Equals method if you want to check for value equality.  But there might be cases when you want to also overload the == operator, having it also check for value equality.  You might do this if your type represents some basic value, like a complex number.

Some guidelines.  Whenever you overload the == operator, you should also:

  • Overload != operator
  • Override Equals method
  • Overload < and > operators

#405 – Equals Method for Equivalence, == Operator for Identity

You can check for equality between two objects by calling the Equals method or by using the == operator.  There are some differences between how the methods work, but both Equals and == can be overloaded in a user-defined type.

You can therefore change the behavior of one of these methods, or both.  The question is–how should these two behaviors work for a custom class?

Roughly speaking, the desired behavior, from a client’s point of view is:

  • Use Equals method to determine value equality, or equivalence–do the two objects represent the same thing?
  • Use == operator to determine reference equality, or identity–do the two references point to exactly the same object?

This argues for overloading Equals for a user-defined reference type, but not overloading the == operator.

This would result in the following behavior:

            Dog d1 = new Dog("Lassie", 7);
            Dog d2 = new Dog("Lassie", 7);

            bool bValueEquality = d1.Equals(d2);    // true
            bool bRefEquality = d1 == d2;    // false

#400 – Overloading Binary Operators

You can overload any of the following binary operators: +, -, *, /, %, &, |, ^, <<, >>.  A binary operator is an operator applied to two operands.

For example, assume we have a Dog class that has the following bool properties: BarksALot, LikesBalls and Sheds.  We might implement the & operator for the Dog class so that we get a new Dog instance with all of the boolean properties AND’d together.

        public static Dog operator &(Dog d1, Dog d2)
        {
            Dog newDog = new Dog(string.Format("{0} & {1}", d1.Name, d2.Name), 0);

            newDog.BarksALot = d1.BarksALot & d2.BarksALot;
            newDog.LikesBalls = d1.LikesBalls & d2.LikesBalls;
            newDog.Sheds = d1.Sheds & d2.Sheds;

            return newDog;
        }

We can now apply this operator to two Dog instances.

            Dog kirby = new Dog("Kirby", 13);
            kirby.LikesBalls = true;
            kirby.BarksALot = true;

            Dog jack = new Dog("Jack", 10);
            jack.Sheds = true;
            jack.BarksALot = true;

            Dog newguy = kirby & jack;

Follow

Get every new post delivered to your Inbox.

Join 37 other followers