SharpShooter

Languages: C#

Technologies: Overloading | Operator Overloading

 

Overload C# Operators

Extend and customize C# to make your apps easier to read and maintain.

 

By Mike Amundsen

 

C# has many great features that make it a powerful and fun language. One of those features is the ability to redefine the functionality and meaning of one or more methods, including the language's built-in methods. This is called overloading. The ability to overload methods in C# gives you the power to design systems that have their own self-consistent syntax and logic. Overloading also allows you to extend the language in ways that were not initially conceived by the folks who implemented the C# language on the .NET platform. In short, overloading is a valuable tool for extending and customizing the C# language for your own needs.

 

You can apply overloading to many situations. In this article, I'll focus on how C# enables you to overload reserved keywords in the language, such as the logical operators (==, !=, <, and >) and the arithmetic operators (+, -, *, /, %). You'll learn how you can redefine the meaning and behavior of these key language operators to help make your own programs more self-consistent and easier to read and maintain over time.

 

A Simple Overloading Example: ToString

Before getting into the details of overloading relational and arithmetic operators, it would be good to start with a simple overloading example: overloading the ToString method. The ToString method is a base-class method inherited by all types and classes defined in C#. It's the method used to come up with a string representation of the type or class with which you are working.

 

For example, this code uses the built-in ToString method to output the value of an integer:

 

Int x = 13;

System.Console.WriteLine(x.ToString());

 

Because all classes and types inherit this method, it's easy for you to call it on any class or type you create. However, if you have created a custom type not previously defined in C#, the ToString method may not know how to output the string properly. FIGURE 1 shows a simple custom type named Point, which holds two values, x and y.

 

// compile string:

// csc /t:library /out:Amundsen.SharpShooter.Overloading.Point.dll Amundsen.SharpShooter.Overloading.Point.cs

 

using System;

 

namespace Amundsen.SharpShooter.Overloading {

 

    public class Point {

 

        private int x;

        private int y;

 

        public Point(int x, int y) {

            this.x=x;

            this.y=y;

        }

 

        public override string ToString() {

            return(String.Format("x={0}, y={1}",x,y));

        }

    }

}

FIGURE 1: This code creates a custom type that overloads the ToString method.

 

You'll notice that there is an overload for the ToString method defined in this class. This new version of ToString will print both the x and y values of the Point object. This overload for ToString basically tells the run time how to behave when a program asks for the string representation of the custom Point object.

 

FIGURE 2 shows an aspx page that uses the compiled assembly from FIGURE 1 to create the Point type and uses the ToString method to produce a string representation of the Point object.

 

<%@ page description="overloading ToString" %>

<%@ import namespace="Amundsen.SharpShooter.Overloading" %>

 

<script language="c#" runat="server">

 

void submit_click(object sender, EventArgs args) {

 

    int x = Int32.Parse(PointX.Text);

    int y = Int32.Parse(PointY.Text);

 

    Point p = new Point(x,y);

    output.Text=p.ToString();

}

 

</script>

 

<html>

<body>

Overloading ToString()

<hr>

 

<form runat="server">

x: <asp:Textbox id="PointX" runat="server"/><br>

y: <asp:Textbox id="PointY" runat="server"/><br>

<asp:button

    Text="Submit"

    id="submit"

    onclick="submit_click"

    runat="server"/>

</form>

 

ToString: <asp:Label id="output" runat="server"/>

 

</body>

</html>

FIGURE 2: This aspx page uses the Point object and the overloaded ToString method.

 

FIGURE 3 shows the resulting page in a Web browser.

 


FIGURE 3: Executing the ToString method of the Point object results in this page.

 

Now that you know how to use overloading to modify the behavior of the built-in ToString method, it's time to learn how to overload relational operators for a custom type or class.

 

Overloading Relational Operators

Although overloading the ToString method for custom types is a common task, there are other overloading-related issues you must consider when creating a custom type or class. For example, you might want to alter the default behavior when comparing two values. This is especially handy when you want to create sorting rules for custom objects. It's relatively simple to sort scalar values such as strings and numbers, but it's not at all clear how you might want objects to be sorted in a list (such as by ID, by name, or by some combination of properties). For this reason, you might want to overload the ==, !=, <, and > operators for your custom type.

 

For example, the code in FIGURE 4 creates a Person class with ID and Name properties. Compile this assembly and place it in a bin folder of your Web.

 

using System;

 

namespace Amundsen.SharpShooter.Overloading {

    public class Person {

        private string _name;

        private int _id;

 

        public Person(int id, string name) {

            _id = id;

             _name = name;

        }

 

        public string Name {

            get {

                return _name;

            }

            set {

                _name=value;

            }

        }

 

        public int ID {

            get {

                return _id;

            }

            set {

                _id = value;

            }

        }

 

        public override string ToString() {

            return(_name+" ("+_id.ToString() +")");

        }

    }

}

FIGURE 4: The custom Person class has ID and Name properties for comparison checking.

 

The person.aspx page shown in FIGURE 5 uses the Person assembly to create two objects with the same ID and Name values to illustrate the results of the equality comparison check.

 

<%@ page description="overloading logical operators" %>

<%@ import namespace="Amundsen.SharpShooter.Overloading" %>

 

<script language="c#" runat="server">

 

void submit_click(object sender, EventArgs args) {

 

     int idA = Int32.Parse(PersonA_ID.Text);

    string nameA = PersonA_Name.Text;

    Person pa = new Person(idA,nameA);

 

    int idB = Int32.Parse(PersonB_ID.Text);

    string nameB = PersonB_Name.Text;

    Person pb = new Person(idB,nameB);

 

    output.Text="<br>"+

        pa.ToString()+"<br>"+

        pb.ToString()+"<br>"+

         (pa==pb).ToString();

}

 

</script>

 

<html>

<body>

 

Overloading Relational Operators

<hr>

 

<form runat="server">

<p>Person A:<br>

ID:<br>

<asp:Textbox id="PersonA_ID" runat="server"/><br>

Name:<br>

<asp:Textbox id="PersonA_Name" runat="server"/><br>

</p>

<p>Person B:<br>

ID:<br>

<asp:Textbox id="PersonB_ID" runat="server"/><br>

Name:<br>

<asp:Textbox id="PersonB_Name" runat="server"/><br>

</p>

 

<asp:button

    Text="Submit"

    id="submit"

    onclick="submit_click"

    runat="server"/>

</form>

 

ToString: <asp:Label id="output" runat="server"/>

 

</body>

</html>

FIGURE 5: This aspx page returns False - that is, non-equality - when asked to compare two identical persons.

 

When you run this page, enter identical values for ID and Name for both objects, and press submit, you'll see that the comparison returns False. You might think that two objects with the same ID and Name settings would be equal, but they are not. That is because the built-in Equals method of the base type compares object locations, not contents. If you wanted these two objects to report True when doing an equality test, you need to overload the Equals method as well as the == and != operators.

 

Operator overloading is relatively simple, and there are only a few rules to keep in mind. First, if you overload the == operator, you also have to overload the != operator. The same is true for the < and > operators. They must be overloaded in pairs in order to maintain consistency. Also, when overloading equality for objects, you need to overload the GetHashCode built-in method. The run time uses the GetHashCode method to support the comparison check, and failure to overload it can produce errors or unwanted results.

 

FIGURE 6 shows the same Person class with the Equals method, GetHashCode method, and the == and != operators overloaded properly.

 

using System;

 

namespace Amundsen.SharpShooter.Overloading {

  public class Person {

    private string _name;

    private int _id;

 

    public Person(int id, string name) {

        _id = id;

        _name = name;

    }

 

    public string Name {

        get {

            return _name;

        }

        set {

            _name=value;

        }

    }

 

    public int ID {

        get {

            return _id;

        }

        set {

            _id = value;

        }

    }

 

    public override string ToString() {

        return(_name+" ("+_id.ToString() +")");

    }

 

    public override bool Equals(object obj) {

        Person p2 = (Person)obj;

        if (_id != p2.ID) {

            return(false);

        }

         if (_name != p2.Name) {

            return(false);

        }

        return(true);

    }

 

    public static bool operator == (Person p1, Person p2) {

        return(p1.Equals(p2));

    }

 

    public static bool operator != (Person p1, Person p2) {

         return(p1.Equals(p2));

    }

 

    public override int GetHashCode() {

        return(_id);

    }

  }

}

FIGURE 6: Adding overloads for Equals, GetHashCode, and the == and != operators.

 

After re-compiling the same Person class from FIGURE 6, you can retest the aspx page from FIGURE 5, and you'll see that the two objects now report that they are equal (see FIGURE 7 for the browser output).

 


FIGURE 7: Testing the Person.aspx page with relational overloading.

 

Overloading Arithmetic Operators

Along with the ability to overload the built-in methods of ToString, GetHashCode, Equals, and the relational operators, you also can overload the arithmetic operators, such as +, -, *, and /. Although this is not a common requirement, there are times when it might come in handy. For example, if you want to create a data type that uses an arithmetic base other than the common base 10, you need to change the behavior of all the arithmetic operators.

 

Consider this: You need to create a class that supports base-five arithmetic. For those not familiar with arithmetic operations on a non-standard base, the base-five counting method looks like this: 0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 20.

 

The ones' place in a base-five system can contain a 0,1,2,3, or 4. However, if you add one more unit to the value of 4, it is represented as 10. That is because in a base-five system, you must place a 1 in the fives' place and a 0 in the ones' place.

 

FIGURE 8 shows the code for a C# struct named obn (for odd base numeral) that does just what we need. It allows users to initialize an instance of the type using an integer value and then performs basic math operations using base-five instead of base-10.

 

namespace Amundsen.SharpShooter.Overloading {

 

    // odd base numeral structure

    public struct obn {

 

        int value;

        int baseValue;

     

        // constructor sets base and accepts integer

        public obn(int value) {

            baseValue=5;

            this.value = value;

        }

 

        // always returns value in the local baseValue

        public override string ToString() {

            return(convertToObn(value).ToString());

        }

 

        // overload the add operator

        public static obn operator +(obn v1, obn v2) {

            return(new obn(v1.value+v2.value));

        }

 

        // overload the subtract operator

        public static obn operator -(obn v1, obn v2) {

            return(new obn(v1.value-v2.value));

        }

 

        // overload the multiply operator

        public static obn operator *(obn v1, obn v2) {

            return(new obn(v1.value*v2.value));

        }

 

        // overload the divide operator

        public static obn operator /(obn v1,obn v2) {

            return(new obn(v1.value/v2.value));

        }

 

        // overload the remainder operator

        public static obn operator %(obn v1, obn v2) {

            return(new obn(v1.value%v2.value));

        }

        // overload the increment operator

        public static obn operator ++(obn v1) {

            return(new obn(v1.value+1));

        }

 

        // overload the decrement operator

        public static obn operator --(obn v1) {

            return(new obn(v1.value-1));

        }

 

        // overload the negation operator

        public static obn operator -(obn v1) {

             return(new obn(-v1.value));

        }

 

        // method to convert to the custom baseValue

        private int convertToObn(int val) {

            int tensValue=(int)val/baseValue;

            int onesValue=(int)val%baseValue;

            int obnValue=(int)(tensValue*10)+onesValue;

            return obnValue;

        }

    }

}

FIGURE 8: Creating a custom type that supports non-standard base arithmetic.

 

In scanning the code in FIGURE 8, you'll notice quite a few operator overloads have been implemented. Not only the basic addition (+), subtraction (-), multiplication (*), and division (/), but also the remainder (%), unary negation (-), increment (++), and decrement (--) operators have been overloaded. You'll also notice the ToString method has been overloaded to call the private convertToObn method that does the real work of converting the integer values to their representations in your custom base-five system.

 

After compiling this custom class and placing it in the bin folder of a Web, you can use the OddBase.aspx page shown in FIGURE 9 to test the base-five arithmetic.

 

<%@ page description="overloading arithmetic operators" %>

<%@ import namespace="Amundsen.SharpShooter.Overloading" %>

 

<script language="C#" runat="server">

 

void submit_click(object sender, EventArgs args) {

 

    int v1 = Int32.Parse(value1.Text);

    int v2 = Int32.Parse(value2.Text);

 

    obn o1 = new obn(v1);

    obn o2 = new obn(v2);

 

    output.Text =

         String.Format("addition.........:

         {0}+{1}={2}",o1,o2,o1+o2)+"<br>"+

        String.Format("subtraction.....:

         {0}-{1}={2}",o1,o2,o1-o2)+"<br>"+

        String.Format("multiplication...:

         {0}*{1}={2}",o1,o2,o1*o2)+"<br>"+

        String.Format("division.........:

         {0}/{1}={2}",o1,o2,o1/o2)+"<br>"+

        String.Format("remainder........:

         {0}%{1}={2}",o1,o2,o1%o2)+"<br>"+

        String.Format("increment........:

         {0}, ++{1}",o2,++o2)+"<br>"+

        String.Format("decrement........:

         {0}, --{1}",o2,--o2)+"<br>"+

         String.Format("negation.........:

         {0}, {1}",o1,-o1)+"<br>";

 

}

 

</script>

 

<html>

<body>

Overloading Arithmetic Operators

<hr>

 

<form runat="server">

Value1:<br>

<asp:Textbox id="value1" runat="server"/><br>

Value2:<br>

<asp:Textbox id="value2" runat="server"/><br>

<asp:Button

    id="submit"

    text="Submit"

    onclick="submit_click"

    runat="server"/>

</form>

 

Results in base 5:<br>

<kbd>

<asp:Label id="output" runat="server"/>

</kbd>

 

<body>

</html>

FIGURE 9: Creating an aspx page to test the arithmetic-overloaded custom type.

 

The aspx page accepts two integer values and then performs a series of arithmetic operations on those numbers using the custom type. FIGURE 10 shows the results in a browser when you enter the integer values 13 and 9.

 


FIGURE 10: Testing the aspx page that performs base-five arithmetic.

 

The files referenced in this article are available for download.

 

Mike Amundsen is president, chief cook, and bottle washer at EraServer.NET, a .NET hosting and XML Web Services company. He also heads a consulting firm, Amundsen.com Inc., which enables clients to design, code, implement, and maintain successful .NET solutions efficiently. A well-known author and lecturer, Mike continues to travel the world teaching and speaking on .NET development issues. His latest book, with Paul Litwin, is ASP.NET for Developers from SAMS. He is also working on the fourth edition of Teach Yourself Database Programming with Visual Basic .NET in 21 Days and has more than a dozen other book titles to his credit. When he's not in the server room or on the road, Mike spends time with his family in and around their home in Northern Kentucky. Readers may contact Mike at mailto:SharpShooter@amundsen.com.

 

Tell us what you think! Please send any comments about this article to editors@devproconnections.com. Please include the article title and author.