The Equals Operator

Although we’ve just warned you away from wildly implementing overloaded arithmetic operators, the comparison operators, especially ==, are another story. It’s very common to overload the == operator to determine whether two objects are equal. What “equal” means is up to you, although your criteria should be reasonable. You might decide that two Employee objects are equal if they have the same name, or you may decide that simply having the same employee ID is sufficient.

Overloading the == operator works the same as overloading any other operator. Simply use the keyword operator with the == symbol, and place your code inside the method. The == operator always returns a Boolean (true or false), so you’ll need to declare the operator as a public static bool. For example, for the Fraction class, your operator might look like this:

public static bool operator== ( Fraction lhs, Fraction rhs )
{
   if ( lhs.denominator == rhs.denominator &&
       lhs.numerator == rhs.numerator )
   {
      return true;
   }
   // code here to handle unlike fractions
   return false;
}

Notice that there’s no else clause here. If the numerator and denominator are equal, the operator returns true, and exits. If they’re not equal, the return statement after the if is executed, so there’s no need for an else.

C# insists that if you overload the equals operator, you must also overload the not-equals operator (!=). It’s good programming practice to have the inequality operator delegate its work to the equality operator, like this:

public static bool operator !=(Fraction lhs, Fraction rhs)
{
   return !(lhs==rhs);
}

As you can see, the != operator will return the opposite of the value of the == operator, which is exactly what you want. This way, if you change your definition of equality, you can change the code in the == operator overload, and the != operator will still return the opposite.

Similarly, the less than (<) and greater than (>) operators must be paired, as must the less than or equal to (<=) and greater than or equal to (>=) operators.

The Object class (which is the root of every class in C#) offers a virtual method called Equals(). (We discuss virtual methods in Chapter 11.) If you overload the equals operator (==), it is recommended that you also override the Equals() method.

Overriding the Equals() method allows your class to be compatible with other .NET languages that do not overload operators (but do support method overloading). That way, even if you can’t use the == operator, you can still use the Equals() method to do the same thing.

The Object class implements the Equals() method with this signature:

public virtual bool Equals(object o)

From this signature, you can see that your override of this method will take an object as a parameter, and return a bool (true if the two objects are equal, where “equality” is however you define it).

By overriding this method, you allow your Fraction class to act polymorphically with all other objects. For example, anywhere you can call Equals() on two Objects, you can call Equals() on two Fractions.

Inside the body of Equals(), you need to ensure that you are comparing one Fraction object with another Fraction object. If the other object is not a fraction, they cannot be equal, and you’ll return false:

public override bool Equals(object o)
{

    if ( ! (o is Fraction) )
    {
        return false;
    }
    return this == (Fraction) o;
}

The is operator is used to check the runtime type of an object (in this case, Fraction). Therefore, o is Fraction evaluates true if o is, in fact, a Fraction or a type derived from Fraction.

Once you know that you are comparing two Fractions, you can delegate the decision as to their equality to the overloaded operator (operator==) that you’ve already written, just as you did with the != operator. This allows you to avoid duplicate code. Notice, though, that before you can compare this to o, you need to cast o to a Fraction. We discussed casting with intrinsic types back in Chapter 3.

In this way, the Equals() method determines only that you do in fact have two fractions. If so, it delegates deciding whether the two fractions are truly equal to the already implemented operator ==.

The complete modification of the Fraction class is shown in Example 12-2, followed by the analysis.

Example 12-2. Implementing the == operator is similar to implementing the addition operator before. However, you also have to implement != and Equals()

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Example_12_2_ _ _ _Overloading_Equality
{
    public class Fraction
    {
        private int numerator;
        private int denominator;

        // create a fraction by passing in the numerator
        // and denominator
        public Fraction(int numerator, int denominator)
        {
            this.numerator = numerator;
            this.denominator = denominator;
        }

        // overloaded operator+ takes two fractions
        // and returns their sum
        public static Fraction operator+ (Fraction lhs, Fraction rhs)
        {
            // like fractions (shared denominator) can be added
            // by adding their numerators
            if (lhs.denominator == rhs.denominator)
            {
                return new Fraction(lhs.numerator + rhs.numerator,
                                    lhs.denominator);
            }

            // simplistic solution for unlike fractions
            // 1/2 + 3/4 == (1*4) + (3*2) / (2*4) == 10/8
            // this method does not reduce.
            int firstProduct = lhs.numerator * rhs.denominator;
            int secondProduct = rhs.numerator * lhs.denominator;
            return new Fraction( firstProduct + secondProduct,
                                lhs.denominator * rhs.denominator );
        }

        // test whether two Fractions are equal
        public static bool operator== (Fraction lhs, Fraction rhs)
        {
            if (lhs.denominator == rhs.denominator &&
                  lhs.numerator == rhs.numerator)
            {
                return true;
            }
            // code here to handle unlike fractions
            return false;
        }

        // delegates to operator ==
        public static bool operator !=(Fraction lhs, Fraction rhs)
        {
            return !(lhs == rhs);
        }

        // tests for same types, then delegates
        public override bool Equals(object o)
        {
            if (!(o is Fraction))
            {
                return false;
            }
            return this == (Fraction)o;
        }

        // return a string representation of the fraction
        public override string ToString()
        {
            String s = numerator.ToString() + "/"
                        + denominator.ToString();
            return s;
        }
    }

    public class Tester
    {
        public void Run()
        {
            Fraction f1 = new Fraction(3, 4);
            Console.WriteLine("f1: {0}", f1.ToString());

            Fraction f2 = new Fraction(2, 4);
            Console.WriteLine("f2: {0}", f2.ToString());

            Fraction f3 = f1 + f2;
            Console.WriteLine("f1 + f2 = f3: {0}", f3.ToString());

            Fraction f4 = new Fraction(5, 4);

            if (f4 == f3)
            {
                Console.WriteLine("f4: {0} == F3: {1}",
                                   f4.ToString(), f3.ToString());
            }

            if (f4 != f2)
            {
                Console.WriteLine("f4: {0} != F2: {1}",
                                   f4.ToString(), f2.ToString());
            }

            if (f4.Equals(f3))
            {
                Console.WriteLine("{0}.Equals({1})",
                                   f4.ToString(), f3.ToString());
            }

        }
        static void Main()
        {
            Tester t = new Tester();
            t.Run();
        }
    }
}

The output looks like this:

f1: 3/4
f2: 2/4
f1 + f2 = f3: 5/4
f4: 5/4 == F3: 5/4
f4: 5/4 != F2: 2/4
5/4.Equals(5/4)

Example 12-2 implements the overloaded equals operator, operator==. If the fractions have the same denominator, you test whether the numerators are equal. If they are, you return true; otherwise, you return false.

public static bool operator== (Fraction lhs, Fraction rhs)
{
    if (lhs.denominator == rhs.denominator &&
          lhs.numerator == rhs.numerator)
    {
        return true;
    }
    // code here to handle unlike fractions
    return false;
}

Tip

We’ve simplified the math here to keep the example readable. Testing for true equality (such as 3/4 = 6/8) is an interesting challenge you might want to try.

This method is invoked in the Run() method when you write:

if (f4 == f3)

The if statement expects a Boolean value, which is what operator== returns. The next thing the class does is implement the != operator, which as we discussed simply returns the opposite of the == operator.

In addition to implementing the == and != operators, you implement the Equals() method, for the reasons explained previously:

public override bool Equals( object o )
{
   if ( !( o is Fraction ) )
   {
      return false;
   }
   return this == (Fraction)o;
}

If the two objects are not both Fractions, you return false; otherwise, you delegate to the == operator, casting o to a Fraction type. Put a breakpoint on the return line, and you’ll find that you step back into operator==. The value returned by operator== is the value returned by the Equals() method if both objects are fractions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset