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 Operators

C#, like any programming language, has a canned set of tokens that are used to perform basic operations on intrinsic types. For example, everyone knows that the + operator can be applied to two integers in order to yield a new integer:

// The + operator in action.

int a = 100;
int b = 240;
int c = a + b; // c = = 340

This is no major news flash, but have you ever stopped and noticed how the same + operator can be applied to any intrinsic C# data type? For example:

// + operator wit with strings.

h string s1 = "Hello";
string s2 = " world!";
string s3 = s1 + s2; // s3 = = Hello world!

In essence, the + operator has been overloaded to function correctly on various  individual data types. When the + operator is applied to numerical types, the result is the summation of the operands. However, when applied to string types, the result is string concatenation. The C# language (like C++ and unlike Java) provides the capability for you to build custom classes and structures that also respond uniquely to the same set of basic tokens (such as the + operator). Thus, if you equip a type to do so, it is possible to apply various operators to a custom class.

To keep your wits about you, assume the following simple Point class:

// You can't get much lamer t than this!

public class Point
{
    private int x, y;
    public Point() { }
    public Point(int xPos, int yPos)
    {
        x = xPos;
        y = yPos;
    }
    public override string ToString()
    {
        return "X pos: " + this.x + " Y pos: " + this.y;
    }
}

Now, logically speaking it makes sense to add Points together. On a related note, it would be helpful to subtract one Point from another. For example, if you created two Point objects with some initial startup values, you would like to do something like this:

// Adding and subtracting two Points.

    public static int Main(string[] args)
    {
        // Make two points
        Point ptOne = new Point(100, 100);
        Point ptTwo = new Point(40, 40);
        // Add the points to make a new point.
        Point bigPoint = ptOne + ptTwo;
        Console.WriteLine("Here is the big point: {0}", bigPoint.ToString());
        // Subtract the points to make a new point.
        Point minorPoint = bigPoint - ptOne;
        Console.WriteLine("Just a minor point: {0}", minorPoint.ToString());
        return 0;
    }

Clearly, your goal is to somehow make your Point class react uniquely to the + and -operators. To allow a custom type to respond to these intrinsic tokens, C# provides the "operator" keyword, which can only be used in conjunction with static methods. To illustrate:

// A more intelligent Point class.
public class Point
{
    private int x, y;
    public Point() { }
    public Point(int xPos, int yPos) { x = xPos; y = yPos; }
    // The Point class can be added…
    public static Point operator +(Point p1, Point p2)
    {
        Point newPoint = new Point(p1.x + p2.x, p1.y + p2.y);
        return newPoint;
    }
    // …and subtracted.
    public static Point operator -(Point p1, Point p2)
    {
        // Figure new X (assume [0,0] base).
        int newX = p1.x - p2.x;
        if (newX < 0)
            throw new ArgumentOutOfRangeException();
        // Figure new Y (also assume [0,0] base).
        int newY = p1.y - p2.y;
        if (newY < 0)
            throw new ArgumentOutOfRangeException();
        return new Point(newX, newY);
    }
    public override string ToString()
    {
        return "X pos: " + this.x + " Y pos: " + this.y;
    }
}

Notice that the class now contains two strange looking methods called operator + and operator –. The logic behind operator + is simply to return a brand new Point based on the summation of the incoming Point objects. Thus, when you write pt1 + pt2, under the hood you can envision the following hidden call to the static operator + method:

// p3 = Point.operator + (p1, p2)
p3 = p1 + p2;
Likewise, p1 - p2 maps to:
// p3 = Point.operator - (p1, p2)
p3 = p1 - p2;

If you were to take your class out for a test run, you would see something like Figure 5-2.



Figure 5-2. Overloaded operators at work

The capability to overload operators is useful in that it enables the object user to work with your types (more or less) like any intrinsic data item. Other languages (such as Java) A Comprehensive Guide to C# and the .NET Platform, ©2001 Andrew Troelsen (Apress, ISBN: 1-893115-59-3) p. 5-7 do not support this capability. Also understand that the capability to overload operators is not a requirement of the Common Language Specification; thus, not all .NET-aware languages support types containing overloaded operators. However, you can achieve the same functionality using public methods. For example, you could write the Point class as
so:

// Making use of methods rather than overloaded ops.
public class Point
{
    // Operator + as AddPoints()
    public static Point AddPoints(Point p1, Point p2)
    {
        return new Point(p1.x + p2.x, p1.y + p2.y);
    }
    // Operator - as SubtractPoints()
    public static Point SubtractPoints(Point p1, Point p2)
    {
        // Figure new X.
        int newX = p1.x - p2.x;
        if (newX < 0)
            throw new ArgumentOutOfRangeException();
        // Figure new Y.
        int newY = p1.y - p2.y;
        if (newY < 0)
            throw new ArgumentOutOfRangeException();
        return new Point(newX, newY);
    }
}

You could then add Points as so:

// As member f(x)'s
Point finalPt = Point.AddPoints AddPoints(ptOne, ptTwo);
Console.WriteLine("My final point: {0}", finalPt.ToString());

Seen in this light, overloaded operators are always an optional construct you may choose to support for a given class. Remember however, that they are little more than a friendly variation on a traditional public method, and are not CLS-compliant. When you are building production level classes that support overloaded operators, you should always support member function equivalents. To maximize your coding efforts, simply have the overloaded operator call the member function alternative (or vice versa). For example:

public class Point
{
    // For overload operator aware languages.
    public static Point operator +(Point p1, Point p2)
    {
        return AddPoints(p1, p2);
    }
    // For overloaded challenged languages.
    public static Point AddPoints(Point p1, Point p2)
    {
        return new Point(p1.x + p2.x, p1.y + p2.y);
    }
}

Total Pages : 11 12345

comments