Fundamentals of Overloading an Operator

Introduction

In elementary school, we learned how to perform arithmetic operations such as adding 2 to 5 to get 7. We also learned somehow how to add letters to create a word, how to add words to create a sentence, and how to add symbols to create abbreviations. In programming, we learn how to create classes that are made of more than one value. When necessary, we may want to add the values of objects of those classes to get new objects or new values. Unfortunately, those classes are not equipped to perform arithmetic operations or comparisons on their objects. Fortunately, if you judge it necessary, you can write code that makes it possible.

We know many of the operators available in the C# language. We also reviewed some of the ways they can be used. We saw that there are rules that must be followed when using any of those operators.

Operator overloading consists of customizing the behavior of a C# operator to be able to apply it on the values of a class. This means that you must create a special behavior in your class for a particular operator you want to use.

A class whose operators you want to overload primarily starts like any class. You can add any members you judge necessary to it. Make sure you provide appropriate and realistic constructors so the class can be initialized appropriately. The other important detail you should take care of is how the value of the class will be accessed outside; that is, by clients of the class. Probably the easiest way to do this consists of overriding the ToString() method in it.

Here is an example of a class:

public class Integer
{
    public int Number;

    public Integer(int n)
    {
        Number = n;
    }

    public override string ToString()
    {
        return Number.ToString();
    }
}

Practical LearningPractical Learning: Introducing Operator Overloading

  1. Start Microsoft Visual Studio
  2. Create a new Console App named Mathematics
  3. In the Solution Explorer, right-click Mathematics -> Add -> New Folder
  4. Type Models and press Enter
  5. In the Solution Explorer, right-click Models -> Add -> Class...
  6. Type Arithmetic and press Enter
  7. Change the document as follows:
    namespace Mathematics
    {
        public class Arithmetic
        {
            public static long GreatestCommonDivisor(long a, long b)
            {
                long remainder;
    
                while (b != 0)
                {
                    remainder = a % b;
                    a = b;
                    b = remainder;
                }
    
                return a;
            }
        }
    }
  8. In the Solution Explorer, right-click Program.cs and click Rename
  9. Type Algebra (to get Algebra.cs) and press Enter
  10. In the Solution Explorer, right-click Models -> Add -> Class...
  11. Type Rational as the name of the new class
  12. Click Add
  13. Change the file as follows:
    namespace Mathematics
    {
        public class Rational
        {
            private long num;
            private long den;
    
            public Rational(long a, long b)
            {
                num = a;
                den = b;
            }
        }
    }
  14. In the document, right-click num -> Refactor -> Encapsulate Field...
  15. In the Encapsulate Field dialog box, change the name to Numerator
  16. Click OK
  17. In the Preview Reference Changes dialog box, check the code and click Apply
  18. In the document, right-click fn -> den -> Encapsulate Field...
  19. In the Encapsulate Field dialog box, change the name to Denominator
  20. Click OK
  21. Inside the class but outside of any method, type public override
  22. In the list that appears, double-click ToString
  23. Complete the file as follows:
    using Mathematics;
    
    namespace Algebra1
    {
        public class Rational
        {
            private long num;
            private long den;
    
            public Rational(long a, long b)
            {
                num = a;
                den = b;
            }
    
            public long Numerator
            {
                get
                {
                    return num / Arithmetic.GreatestCommonDivisor(num, den);
                }
                
                set { num = value; }
            }
    
            public long Denominator
            {
                get
                {
                    return den / Arithmetic.GreatestCommonDivisor(num, den);
                }
    
                set { den = value; }
            }
    
            public override string ToString()
            {
                long numer = num / Arithmetic.GreatestCommonDivisor(num, den);
                long denom = den / Arithmetic.GreatestCommonDivisor(num, den);
    
                if (denom == 1)
                    return numer.ToString();
                else
                    return string.Format("{0}/{1}", numer, denom);
            }
        }
    }

The Formula

To overload an operator, you must create a static method using the following formula:

public static ReturnType operator What(Argument(s))
{
}

Start with the access level. This is public. After the public access level, use the static keyword.

Because the method is statically implemented, you should use the name of its class as the return type. After all, the operator will act on an object of its class type. For this reason, the method should return a value that represents its class.

After the return type, you must use the operator keyword. This is followed by the operator you want to customize. There are rules you must, and suggestions you should, follow:

Because you are creating a type of method, after the operator, add the parentheses. In the parentheses, you will add one or more arguments, depending on the operator.

Since this is a method, it must have a body delimited by curly brackets.

After defining the method, you can call it either inside or outside of its class.

Overloading an Operator

Unary Operators

A unary operator is one that acts on one value. The C# language provides many unary operators but not all can be overloaded. The unary operarors available for overloading are +, -, !, ~, ++, and --.

To overload a unary operator, pass one argument to the parentheses of the method. In the body of the method, perform the desired operation. Here is an example of overloading a unary operaor:


public class Integer
{
    public int Number;

    public Integer(int n)
    {
        Number = n;
    }

    public static Integer operator ~(Integer value)
    {
        int number = value.Number;
        int nbr = ~number;

        Integer natural = new Integer(nbr);

        return natural;
    }

    public override string ToString()
    {
        return Number.ToString();
    }
}

public class Exercise
{
    static int Main()
    {
        Integer nat = new Integer(1405);

        Console.WriteLine("The bitwise negation of {0} is {1}",
            nat, ~nat);

        return 0;
    }
}

This would produce:

The bitwise negation of 1405 is -1406
Press any key to continue . . .

In the same way, you can overload the other operators.

Practical LearningPractical Learning: Overloading Unary Operators

  1. Change the Rational class as follows:
    
    
    using System.Text;
    using Mathematics;
    
    namespace Algebra1
    {
        public class Rational
        {
            private long num;
            private long den;
    
            public Rational(long a, long b)
            {
                num = a;
                den = b;
            }
    
            public long Numerator
            {
                get
                {
                	return num / Arithmetic.GreatestCommonDivisor(num, den);
                }
    
                set { num = value; }
            }
    
            public long Denominator
            {
                get
                {
                    return den / Arithmetic.GreatestCommonDivisor(num, den);
                }
    
                set { den = value; }
            }
    
            public static Rational operator ++(Rational number)
            {
                long result = number.Numerator + number.Denominator;
    
                return new Rational(result, number.Denominator);
            }
    
            public static Rational operator --(Rational number)
            {
                long result = number.Numerator - number.Denominator;
    
                return new Rational(result, number.Denominator);
            }
             
            public override string ToString()
            {
                long numer = num / Arithmetic.GreatestCommonDivisor(num, den);
                long denom = den / Arithmetic.GreatestCommonDivisor(num, den);
    
                if (denom == 1)
                    return numer.ToString();
                else
                    return string.Format("{0}/{1}", numer, denom);
            }
        }
    }
  2. Access the Algebra.cs file and change it as follows:
    using Algebra1;
    
    public class Exercise
    {
        public static int Main()
        {
            long x, y;
            Rational fraction = null;
    
            Console.WriteLine("This program allows you to get a rational");
            Console.Write("Enter the numerator:   ");
            x = long.Parse(Console.ReadLine());
            Console.Write("Enter the denominator: ");
            y = long.Parse(Console.ReadLine());
            Console.WriteLine();
    
            fraction = new Rational(x, y);
            Console.WriteLine("Fraction:  {0}/{1} is equivalent to {2}\n",
                fraction.Numerator, fraction.Denominator, fraction);
    
            fraction = new Rational(x, y);
            Rational increment = ++fraction;
            Console.WriteLine("Increment: {0}/{1}++ = {2}",
                x, y, increment);
    
            fraction = new Rational(x, y);
            Rational decrement = --fraction;
            Console.WriteLine("Decrement: {0}/{1}-- = {2}",
                x, y, decrement);
    
            System.Console.ReadKey();
            return 0;
        }
    }
  3. To execute the application, on the main menu, click Debug -> Start Debugging
  4. When requested, enter the numerator as 128 and press Enter
  5. Enter the denominator as 54 and press Enter
    This program allows you to get a rational
    Enter the numerator:   128
    Enter the denominator: 54
    
    Fraction:  128/54 is equivalent to 64/27
    
    Increment: 128/54++ = 91/27
    Decrement: 128/54-- = 37/27

Binary Arithmetic Operators

A binary operator is one that acts on two values. The values must be of the same type. C# provides a rich set of binary operators. Those used in arithmetics are: +, -, *, /, and %.

To overloaded a binary arithmetic operator, pass two arguments to the parentheses of the method. The first arguments must be of the type of class in which you are working. The second argument can be another type. Here is an example:


public class Integer
{
    public int Number;

    public Integer(int n)
    {
        Number = n;
    }

    public static Integer operator ~(Integer value)
    {
        int number = value.Number;
        int nbr = ~number;

        Integer natural = new Integer(nbr);

        return natural;
    }

    public static Integer operator +(Integer value, int add)
    {
        int number = value.Number;
        int nbr = number + add;

        Integer natural = new Integer(nbr);

        return natural;
    }

    public override string ToString()
    {
        return Number.ToString();
    }
}

public class Exercise
{
    static int Main()
    {
        Integer nat = new Integer(248);

        Console.WriteLine("{0} + 35 = {1}", nat, nat + 35);
        return 0;
    }
}

This would produce:

248 + 35 = 283
Press any key to continue . . .

If you want to perform the operations on two similar types of values, then the second argument must be of the same type as the first, which is the class you are using.

Practical LearningPractical Learning: Overloading Binary Operators

  1. Access the Rational.cs file and change it as follows:
    
    
    using System.Text;
    using Mathematics;
    
    namespace Algebra1
    {
        public class Rational
        {
            private long num;
            private long den;
    
            public Rational(long a, long b)
            {
                num = a;
                den = b;
            }
    
            public long Numerator
            {
                get
                {
                    return num / Arithmetic.GreatestCommonDivisor(num, den);
                }
                
                set { num = value; }
            }
    
            public long Denominator
            {
                get
                {
                    return den / Arithmetic.GreatestCommonDivisor(num, den);
                }
    
                set { den = value; }
            }
    
            public static Rational operator ++(Rational number)
            {
                long result = number.Numerator + number.Denominator;
    
                return new Rational(result, number.Denominator);
            }
    
            public static Rational operator --(Rational number)
            {
                long result = number.Numerator - number.Denominator;
    
                return new Rational(result, number.Denominator);
            }
    
            public static Rational operator +(Rational first, Rational second)
            {
                long numerator = (first.Numerator * second.Denominator) + (first.Denominator * second.Numerator);
                long denominator = first.Denominator * second.Denominator;
    
                return new Rational(numerator, denominator);
            }
    
            public static Rational operator -(Rational first, Rational second)
            {
                long numerator = (first.Numerator * second.Denominator) - (first.Denominator * second.Numerator);
                long denominator = first.Denominator * second.Denominator;
    
                return new Rational(numerator, denominator);
            }
    
            public static Rational operator *(Rational first, Rational second)
            {
                long numerator = first.Numerator * second.Numerator;
                long denominator = first.Denominator * second.Denominator;
    
                return new Rational(numerator, denominator);
            }
    
            public static Rational operator /(Rational first, Rational second)
            {
                
                long numerator = first.Numerator * second.Denominator;
                long denominator = first.Denominator * second.Numerator;
    
                return new Rational(numerator, denominator);
            }
             
            public override string ToString()
            {
                long numer = num / Arithmetic.GreatestCommonDivisor(num, den);
                long denom = den / Arithmetic.GreatestCommonDivisor(num, den);
    
                if (denom == 1)
                    return numer.ToString();
                else
                    return string.Format("{0}/{1}", numer, denom);
            }
        }
    }
  2. Access the Algebra.cs file and change it as follows:
    using Algebra1;
    
    public class Exercise
    {
        public static int Main()
        {
            long a, b, c, d;
            Rational firstFraction = null;
            Rational secondFraction = null;
    
            Console.Write("This program allows you to perform ");
            Console.WriteLine("an arithmetic operation on two fractions");
            Console.WriteLine("First Fraction");
            Console.Write("\tEnter the numerator:   ");
            a = long.Parse(Console.ReadLine());
            Console.Write("\tEnter the denominator: ");
            b = long.Parse(Console.ReadLine());
            Console.WriteLine("Second Fraction");
            Console.Write("\tEnter the numerator:   ");
            c = long.Parse(Console.ReadLine());
            Console.Write("\tEnter the denominator: ");
            d = long.Parse(Console.ReadLine());
    
            Console.WriteLine();
    
            firstFraction = new Rational(a, b);
            Console.WriteLine("First Fraction:   {0}/{1} = {2}\n",
                a, b, firstFraction);
            secondFraction = new Rational(c, d);
            Console.WriteLine("Second Fraction:  {0}/{1} = {2}\n",
                c, d, secondFraction);
    
            Console.WriteLine();
    
            Rational addition = firstFraction + secondFraction;
            Console.WriteLine("Addition: {0}/{1} + {2}/{3} = {4}",
                a, b, c, d, addition);
            Rational subtraction = firstFraction - secondFraction;
            Console.WriteLine("Addition: {0}/{1} - {2}/{3} = {4}",
                a, b, c, d, subtraction);
            Rational multiplication = firstFraction * secondFraction;
            Console.WriteLine("Addition: {0}/{1} * {2}/{3} = {4}",
                a, b, c, d, multiplication);
            Rational division = firstFraction / secondFraction;
            Console.WriteLine("Addition: {0}/{1} / {2}/{3} = {4}",
                a, b, c, d, division);
    
            System.Console.ReadKey();
            return 0;
        }
    }
  3. To execute the application, on the main menu, click Debug -> Start Debugging
  4. When requested, enter the first numerator as 128 and press Enter
  5. Enter the first denominator as 54 and press Enter
  6. Enter the second numerator as 88 and press Enter
  7. Enter the second denominator as 36 and press Enter
    This program allows you to perform an arithmetic operation on two fractions
    First Fraction
            Enter the numerator:   128
            Enter the denominator: 54
    Second Fraction
            Enter the numerator:   88
            Enter the denominator: 36
    
    First Fraction:   128/54 = 64/27
    
    Second Fraction:  88/36 = 22/9
    
    Addition: 128/54 + 88/36 = 130/27
    Addition: 128/54 + 88/36 = -2/27
    Addition: 128/54 + 88/36 = 1408/243
    Addition: 128/54 + 88/36 = 32/33

Binary Bitwise Operators

Besides the traditional arithmetic operations, C# support the ability to add or shift bits in the computer memory. These operators are the bitwise conjunction operator & and its  assignment operator &=, the bitwise disjunction operator | and its assignment operator |=, the bitwise exclusion operator ^ and its assignment operator ^=, the left-shift operator << and its assigment operator <<=, the right-shift operator >> and its assignment operator >>=. When necessary, overload only the primary operator (&, |, ^, <<, >>) and its equivalent assignment operator will be automatically provided by the compiler.

To overloaded a binary operator, pass two arguments to the parentheses of the method. For the two bitwise shift operators, << and >>, the first argument must be the class in which you are working and the second argument must by a constant integer (type int). Here is an example that overloads the left bitwise shifter <<:


public class Integer
{
    public int Number;

    public Integer(int n)
    {
        Number = n;
    }

    public static Integer operator ~(Integer value)
    {
        int number = value.Number;
        int nbr = ~number;

        Integer natural = new Integer(nbr);

        return natural;
    }

    public static Integer operator <<(Integer value, int shifts)
    {
        int number = value.Number;
        int nbr = (number << shifts );

        Integer natural = new Integer(nbr);

        return natural;
    }

    public override string ToString()
    {
        return Number.ToString();
    }
}

public class Exercise
{
    static int Main()
    {
        Integer nat = new Integer(1248);

        Console.WriteLine("Shifting the bits of {0} to the left by {1} bits is {2}",
            nat, 4, nat << 4);

        return 0;
    }
}

This would produce:

Shifting the bits of 1248 to the left by 4 bits is 19968
Press any key to continue . . .

For the operators that join or disjoin two series of bits:

In the same way you can overload the other operators.

Comparison Operators

As its name implies, a comparison is used to find out whether one of two values is higher than the other. The values must be of the same type. Comparison operators go in pair. That is, if you want to compare two values for equality, you must also be able to know when they are different. For this reason, if you decide to overload a comparison operator, you must also overload its opposite:

We saw that the Object class is equipped with a method named Equals. That method makes it possible to find out whether one object is equal to another. And since all classes in a C# application derive from Object, if you decide to overload a couple of comparison operators, you should also override the Equals() method. 

Practical LearningPractical Learning: Overloading Binary Operators

  1. Access the Rational.cs file and change it as follows:
    
    
    using System.Text;
    using Mathematics;
    
    namespace Algebra1
    {
        public class Rational
        {
            private long num;
            private long den;
    
            public Rational(long a, long b)
            {
                num = a;
                den = b;
            }
    
            public long Numerator
            {
                get
                {
                    return num / Arithmetic.GreatestCommonDivisor(num, den);
                }
    
                set { num = value; }
            }
    
            public long Denominator
            {
                get
                {
                    return den / Arithmetic.GreatestCommonDivisor(num, den);
                }
    
                set { den = value; }
            }
    
            public static Rational operator ++(Rational number)
            {
                long result = number.Numerator + number.Denominator;
    
                return new Rational(result, number.Denominator);
            }
    
            public static Rational operator --(Rational number)
            {
                long result = number.Numerator - number.Denominator;
    
                return new Rational(result, number.Denominator);
            }
    
            public static Rational operator +(Rational first, Rational second)
            {
                long numerator = (first.Numerator * second.Denominator) +
                                 (first.Denominator * second.Numerator);
                long denominator = first.Denominator * second.Denominator;
    
                return new Rational(numerator, denominator);
            }
    
            public static Rational operator -(Rational first, Rational second)
            {
                long numerator = (first.Numerator * second.Denominator) -
                                 (first.Denominator * second.Numerator);
                long denominator = first.Denominator * second.Denominator;
    
                return new Rational(numerator, denominator);
            }
    
            public static Rational operator *(Rational first, Rational second)
            {
                long numerator = first.Numerator * second.Numerator;
                long denominator = first.Denominator * second.Denominator;
    
                return new Rational(numerator, denominator);
            }
    
            public static Rational operator /(Rational first, Rational second)
            {
                long numerator = first.Numerator * second.Denominator;
                long denominator = first.Denominator * second.Numerator;
    
                return new Rational(numerator, denominator);
            }
    
            public static bool operator ==(Rational first, Rational second)
            {
                if( (first.Numerator == second.Numerator) &&
                    (first.Denominator == second.Denominator) )
                    return true;
                else
                    return false;
            }
    
            public static bool operator !=(Rational first, Rational second)
            {
                if( (first.Numerator != second.Numerator) ||
                    (first.Denominator != second.Denominator) )
                    return true;
                else
                    return false;
            }
             
            public override string ToString()
            {
                long numer = num / Arithmetic.GreatestCommonDivisor(num, den);
                long denom = den / Arithmetic.GreatestCommonDivisor(num, den);
    
                if (denom == 1)
                    return numer.ToString();
                else
                    return string.Format("{0}/{1}", numer, denom);
            }
    
            public override bool Equals(object obj)
            {
                Rational rat = (Rational)obj;
    
                if( (this.num  == rat.num) &&
                    (this.den  == rat.den) )
                    return true;
    
                return false;
            }
        }
    }
  2. Access the Algebra.cs file and change it as follows:
    using Algebra1;
    
    public class Exercise
    {
        public static int Main()
        {
            long a, b, c, d;
            Rational firstFraction = null;
            Rational secondFraction = null;
    
            Console.Write("This program allows you to perform ");
            Console.WriteLine("an arithmetic operation on two fractions");
            Console.WriteLine("First Fraction");
            Console.Write("\tEnter the numerator:   ");
            a = long.Parse(Console.ReadLine());
            Console.Write("\tEnter the denominator: ");
            b = long.Parse(Console.ReadLine());
            Console.WriteLine("Second Fraction");
            Console.Write("\tEnter the numerator:   ");
            c = long.Parse(Console.ReadLine());
            Console.Write("\tEnter the denominator: ");
            d = long.Parse(Console.ReadLine());
    
            Console.WriteLine();
    
            firstFraction = new Rational(a, b);
            Console.WriteLine("First Fraction:   {0}/{1} = {2}\n",
                              a, b , firstFraction);
            secondFraction = new Rational(c, d);
            Console.WriteLine("Second Fraction:  {0}/{1} = {2}\n",
                              c, d, secondFraction);
    
            Console.WriteLine();
    
            Console.WriteLine("{0}/{1} == {2}/{3} = {4}",
                a, b, c, d, firstFraction == secondFraction);
            Console.WriteLine("{0}/{1} != {2}/{3} = {4}",
                a, b, c, d, firstFraction != secondFraction);
    
            System.Console.ReadKey();
            return 0;
        }
    }
  3. To execute the application, on the main menu, click Debug -> Start Debugging
  4. When requested, enter the first numerator as 36 and press Enter
  5. Enter the first denominator as 8 and press Enter
  6. Enter the second numerator as 54 and press Enter
  7. Enter the second denominator as 12 and press Enter
    This program allows you to perform an arithmetic operation on two fractions
    First Fraction
            Enter the numerator:   36
            Enter the denominator: 8
    Second Fraction
            Enter the numerator:   54
            Enter the denominator: 12
    
    First Fraction:   36/8 = 9/2
    
    Second Fraction:  54/12 = 9/2
    
    
    36/8 == 54/12 = True
    36/8 != 54/12 = False
  8. To execute the application again, on the main menu, click Debug -> Start Debugging
  9. When requested, enter the first numerator as 36 and press Enter
  10. Enter the first denominator as 14 and press Enter
  11. Enter the second numerator as 45 and press Enter
  12. Enter the second denominator as 20 and press Enter
    This program allows you to perform an arithmetic operation on two fractions
    First Fraction
            Enter the numerator:   26
            Enter the denominator: 14
    Second Fraction
            Enter the numerator:   45
            Enter the denominator: 20
    
    First Fraction:   26/14 = 13/7
    
    Second Fraction:  45/20 = 9/4
    
    
    26/14 == 45/20 = False
    26/14 != 45/20 = True

Restricted Operators

Some operators either cannot be overloaded or depend on other operators being overloaded:

Practical LearningPractical Learning: Ending the Lesson

  1. Close your programming environment
  2. When asked whether you want to save, click No

Previous Copyright © 2001-2024, FunctionX Friday 15 October 2021 Next