Section 12.3. The Equals Operator


12.3. The Equals Operator

The Object class (which is the root of every class in C#) offers a virtual method called Equals( ) . (Virtual methods are discussed 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).

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 defined by the creator of the class).

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 whether the runtime type of an object is compatible with the operand (in this case, Fraction ). Thus o is Fraction evaluates true if o is, in fact, a Fraction or a type derived from Fraction .

If you are comparing two Fractions , you can delegate the decision as to their equality to the overloaded operator ( operator== ) that you've already written. This allows you to avoid duplicate code.

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

In this way, the Equals( ) method determines only that you do in fact have two fractions. If so, it delegates deciding if 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 equality operators
 using System; 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 thier 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 starts by implementing 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.

This is a mathematical simplification to keep the example readable. Testing for true equality (such as 3/4 = 6/8) is left as an exercise for the reader.


 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;     } 

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.

It is quite common to overload the equals operator ( == ) to test whether two objects are equal. C# insists that if you overload the equals operator, you must also overload the not-equals operator ( != ). 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.

It is good programming practice to have the inequality operator delegate its work to the equality operator, so that if you change the definition of equality, you are assured that the inequality operator will use the new definition.

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

This operator is invoked in Run( ) when you write:

 if (f4 != f2) 

Put a breakpoint on this line of code and run to this line in Visual Studio .NET, as shown in Figure 12-1. (For more about breakpoints, see Chapter 9.)

Figure 12-1. Running to the breakpoint

Press F11 to step into the method callyou'll step into the != operator at the return statement. Press F11 again and step into the == operator. Whatever value is returned by the == operator is negated when it is returned by the != operator. If false is returned by == , then true is returned by != .

You can make the reversal of equality explicit by adding a temporary Boolean variable named "equality." Rewrite the != operator as follows :

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

You can now put a breakpoint on the second line of this method and examine the value returned by operator== , as shown in Figure 12-2.

Figure 12-2. Examining the interim value

You can see in the Autos window that the value of equality (shown circled and highlighted) is false. The fractions have been expanded to show their values (5/4 and 2/4), and they are not equal. The value returned by the != operator is the opposite of false; that is, true.

In addition to implementing the == and != operator, 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.

The Meaning of Equality

It is up to you, as the class designer, to decide what it means for two instances of your class to be equal. Two employees might be equal if they have the same name , or you might decide they are only equal if they have the same Employee ID.




Learning C# 2005
Learning C# 2005: Get Started with C# 2.0 and .NET Programming (2nd Edition)
ISBN: 0596102097
EAN: 2147483647
Year: 2004
Pages: 250

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net