FREE BOOK

Chapter 5: Advanced C# Class Construction Techniques

Posted by Apress Free Book | C# Language January 13, 2009
This chapter rounds out your introduction to the core aspects of the C# language by examining a number of advanced (but extremely useful) syntactic constructs. To begin, you learn how to construct and use an indexer method.

Overloading the Equality Operators

As you recall, System.Object.Equals() can be overridden in order to perform value-based (rather than referenced-based) comparisons between objects. In addition to overriding Equals() and GetHashCode(), an object may choose to override the equality operators (= = and !=).To illustrate, here is the updated Point class:

// This incarnation of Point also overloads the = = and != operators.
public class Point
{
public int x, y;
public Point(){}
public Point(int xPos, int yPos){x = xPos; y = yPos;}
public override bool Equals(object o)
{
if( ((Point)o).x = = this.x &&
((Point)o).y = = this.y)
return true;
else
return
false;
}
public override int GetHashCode()
{ return this.ToString().GetHashCode(); }
// Now let's overload the = = and != operators.
public static bool operator = =(Point p1, Point p2)
{
return p1.Equals(p2);

public static bool operator !=(Point p1, Point p2)
{
return !p1.Equals(p2);
}
}

Notice how the implementation of operator = = and operator != simply makes a call to the overridden Equals() method to get the bulk of the work done. Given this, you can now exercise your Point class as so:

// Make use of the overloaded equality operators.

public static int Main(string[] args)
{
if(ptOne = = ptTwo ptTwo) // Are they the same?
Console.WriteLine("Same values!");
else
Console.WriteLine("Nope, different values.");
if(ptOne != ptTwo ptTwo) // Are they different?
Console.WriteLine("These are not equal.");
else
Console.WriteLine("Same values!");
}

As you can see, it is quite intuitive to compare two objects using the well-known = = and != operators rather than making a call to Object.Equals(). As a rule of thumb, classes that override Object.Equals() should always overload the = = and !+ operators. If you do overload the equality operators for a given class, keep in mind that C# demands that if you override operator = =, you must also override operator !=, just as when you override Equals() you will need to override GetHashCode(). This ensures that an object behaves in a uniform manner during comparisons and functions correctly if placed into a hash table (if you forget, the compiler will let you know).

SOURCE CODE The OverLoadOps project is located under the Chapter 5 subdirectory.

Overriding the Comparison Operators

In the previous chapter, you learned how to implement the IComparable interface, in order to compare the relative relationship between two like objects. Additionally, you may also overload the comparison operators (<, >, <= and >=) for the same class. Like the equality operators, C# demands that < and > are overloaded as a set. The same holds true for the <= and >= operators. If the Car type you developed in Chapter 4 overloaded these comparison operators, the object user could now compare types as so:

// Exercise the overloaded < operator for the Car class.
public class CarApp
{
    public static int Main(string[] args)
{
// Make an array of Car types.
Car[] myAutos = new Car[5];
myAutos[0] = new Car(123, "Rusty");
myAutos[1] = new Car(6, "Mary");
myAutos[2] = new Car(6, "Viper");
myAutos[3] = new Car(13, "NoName");
myAutos[4] = new Car(6, "Chucky");
// Is Rusty less than Chucky?
if(myAu myAutos[0] < myAutos[4] tos[4])
Console.WriteLine("Rusty is less than Chucky!");
else
Console.WriteLine("Chucky is less than Rusty!");
return 0;
}
}

Because the Car type already implements IComparable (see Chapter 4), overloading the comparison operators is trivial. Here is the updated class definition:

// This class is also comparable using the comparison operators.
public class Car : IComparable
{
 
    public int CompareTo(object o)
    {
        Car temp = (Car)o;
        if (this.CarID > temp.CarID)
            return 1;
        if (this.CarID < temp.CarID)
            return -1;
        else
            return 0;
    }
    public static bool operator <(Car c1, Car c2)
    {
        IComparable itfComp = (IComparable)c1;
        return (itfComp.CompareTo(c2) < 0);
    }
    public static bool operator >(Car c1, Car c2)
    {
        IComparable itfComp = (IComparable)c1;
        return (itfComp.CompareTo(c2) > 0);
    }
    public static bool operator <=(Car c1, Car c2)
    {
        IComparable itfComp = (IComparable)c1;
        return (itfComp.CompareTo(c2) <= 0);
    }
    public static bool operator >=(Car c1, Car c2)
    {
        IComparable itfComp = (IComparable)c1;
        return (itfComp.CompareTo(c2) >= 0);
    }
}

SOURCE CODE The ObjCompWithOps project is located under the Chapter 5 subdirectory.

Final Thoughts Regarding Operator Overloading

As you have just seen, C# provides the capability to build types that can respond uniquely to various intrinsic, well-known operators. Now, before you go and retrofit all your classes to support such behavior, you must be sure that the operator(s) you are about to overload make some sort of logical sense in the world at large.

For example, let's say you overloaded the multiplication operator for the Engine class. What exactly would it mean to multiply two Engine objects? Not much.
Overloading operators is generally only useful when building utility types. Strings, points, rectangles, fractions, and hexagons make good candidates for operator
overloading. People, managers, cars, headphones, and baseball hats do not. Use this feature wisely.

Also, always remember that not all languages targeting the .NET platform will support overloaded operators for custom types! Therefore, always test your types against any language that may make use of a class defining overloaded operators. If you want to be completely sure that your types will work in any .NET-aware language, supply the same functionality using custom methods in addition to your operator set (as illustrated earlier in this chapter).

Finally, be aware that you cannot overload each and every intrinsic C# operator.

Table 5-1 outlines the "overloadability" of each item:

C#
Operator

Meaning in Life
(Can this operator be overloaded?)

+
-
!
~
++
--
true
false

This set of unary operators can be overloaded.

+
-
*
/
%
&
|
^
<<
>>

These binary operators can be overloaded.

= =
!=
<
>
<=
>=

The comparison operators can be overloaded. Recall, however, the C# will
demand that "like" operators (i.e., < and >, <= and >=, = =, and !=) are
overloaded together.

[]

The [] operator cannot technically be overloaded. As you have seen earlier in this
chapter, however, the indexer construct provides the same functionality.

Total Pages : 11 12345

comments