C# Operator Overloading

Implementing popular functionalities multiple times with different parameter lists is commonly referred to as overloading.

Object-oriented programming principles like Polymorphism involve the concept of a single name having multiple forms and behaviors. This feature enables variables or objects to adapt to different forms during code execution. Polymorphism is particularly useful when you need a method's behavior to vary based on the provided arguments, allowing for diverse sequences of execution. This flexibility allows a single name to interact with various execution methods and properties. Achieving this in a program involves leveraging different parameter types and quantities. C# offers a range of operator overloading techniques, which will be comprehensively covered in this instructional guide.

Operator Overloading Techniques in C#

Operator overloading in C# involves utilizing various types of operators. However, prior to delving into the methodologies, it is important to first understand the verifications of operators and their application in the process of operator overloading.

The CSS code snippet below demonstrates the styling for a placeholder diagram. This includes a background gradient, border radius, padding, margin, and text alignment. The diagram contains an icon with a specific font size and margin, along with text styled in a particular color and font size.

Syntax

Example

public static classname  operator op (parameters)
{
// Code
}

For Unary Operator

public static classname operator op (t)
{
// Code
}

For Binary Operator

public static classname operator op (t1, t2)
{
// Code
}

The operator keyword is commonly utilized when implementing operator overloading. It is crucial to highlight that the return type of the overloaded operator cannot be void. This signifies that during operator overloading, the prioritization lies with the user-defined implementations. Predefined implementations do not receive any preference.

Thus, the overloaded functions must feature a distinct combination of parameters. The sequence and quantity of arguments should vary. While they resemble regular methods, the hierarchy and syntax of user-defined functions remain fixed. The operand positioned on the left side is referred to as a member in binary operators, whereas the one on the right is denoted as a parameter. A deeper comprehension of these principles will be attained as you delve into the corresponding implementation.

An additional crucial point to note is that within C#, there exists a distinct function referred to as the operator function. This particular function must consistently be both static and public. It exclusively accepts value arguments, typically excluding outer and reference parameters. The standard syntax for implementing an operator function adheres to the subsequent format.

Example

public static return_type operator op (argument list)

Here, the op serves as the operator employed for overloading, with the operator being the essential keyword. For a unary operator, a single argument is passed, while a binary operator requires two arguments. It is crucial to emphasize that at least one argument must be of a user-defined type, struct type, or class. Let's delve into the implementation of these principles.

Unary Operator Overloading

The standard formats for implementing operator overloading with unary operators adhere to the following syntax.

Example

public static return_type operator op (Type t)  
{  
// Statements  
}

Here, the output type is flexible, except it must not be void for operators such as addition (+), subtraction (-), bitwise NOT (~), and member access (dot). The return type of these operators should specifically be either an integer or a Boolean type. It is crucial to understand that Boolean operators yield true or false results, consequently, they can only be overloaded in pairs. Failure to do so would result in a compilation error as a class usually defines one of these operators without defining the counterpart.

Consider the subsequent code snippet demonstrating the overloading of unary operators within a class named Complex.

Example

using System;  
class Complex  
{  
    private int x;  
    private int y;  
    public Complex()  
    {  
    }  
    public Complex(int i, int j)  
    {  
        x = i;  
        y = j;  
    }  
    public void ShowXY()  
    {  
        Console.WriteLine("{0} {1}", x, y);  
    }  
    public static Complex operator -(Complex c)  
    {  
        Complex temp = new Complex();  
        temp.x = -c.x;  
        temp.y = -c.y;  
        return temp;  
    }  
}  
class MyClient  
{  
    public static void Main()  
    {  
        Complex c1 = new Complex(10, 20);  
        c1.ShowXY(); // displays 10 & 20  
        Complex c2 = new Complex();  
        c2.ShowXY(); // displays 0 & 0  
        c2 = -c1;  
        c2.ShowXY(); // diapls -10 & -20  
    }  
}

Binary Operator Overloading

To implement overloading for a binary operator, you must specify two parameters. It is crucial that one of the operands is of the class or struct type in which the operator is declared. As mentioned earlier, a binary operator cannot have a void return type, but it can return any other data type when overloading is implemented. The typical syntax for overloading binary operators can be illustrated as follows.

Example

public static return_type operator op (Type1 t1, Type2 t2)  
{  
//Statements  
}

Examine this provided code snippet demonstrating the functionality of binary operator overloading.

Example

using System;  
class Complex  
{  
    private int x;  
    private int y;  
    public Complex()  
}  
public Complex(int i, int j)  
{  
    x = i;  
    y = j;  
}  
public void ShowXY()  
{  
    Console.WriteLine("{0} {1}", x, y);  
}  
public static Complex operator +(Complex c1, Complex c2)  
{  
    Complex temp = new Complex();  
    temp.x = c1.x + c2.x;  
    temp.y = c1.y + c2.y;  
    return temp;  
}  
}  
class MyClient  
{  
    public static void Main()  
    {  
        Complex c1 = new Complex(10, 20);  
        c1.ShowXY(); // displays 10 & 20  
        Complex c2 = new Complex(20, 30);  
        c2.ShowXY(); // displays 20 & 30  
        Complex c3 = new Complex();  
        c3 = c1 + c2;  
        c3.ShowXY(); // dislplays 30 & 50  
    }  
}

It's crucial to remember that operators like ==, !=, <>, and <=, >= are exclusively overloaded in pairs. When a binary arithmetic operator is overloaded using these, the assignment operators will be automatically overloaded as well. For example, overloading the + operator will also implicitly overload the += operator.

Operator overloading & Inheritance

Another scenario arises when declaring overloaded operators as static, leading to their inheritance by the derived class. This occurs due to the requirement that the operator's declaration must be within a struct or class where the operator is being defined. This ensures the consistency of the operator's signature across classes. Consequently, a previously declared operator in the derived class cannot override an operator existing in the parent class. Therefore, the use of the new modifier is not feasible in this context as it is not permitted during operator declaration. The following code snippet exemplifies this particular situation.

Example

using System;  
class Complex  
{  
    private int x;  
    private int y;  
    public Complex()  
    {  
    }  
    public Complex(int i, int j)  
    {  
        x = i;  
        y = j;  
    }  
    public void ShowXY()  
    {  
        Console.WriteLine("{0} {1}", x, y);  
    }  
    public static Complex operator +(Complex c1, Complex c2)  
    {  
        Complex temp = new Complex();  
        temp.x = c1.x + c2.x;  
        temp.y = c1.y + c2.y;  
        return temp;  
    }  
}  
class MyComplex: Complex  
{  
    private double x;  
    private double y;  
    public MyComplex(double i, double j)  
    {  
        x = i;  
        y = j;  
    }  
    public MyComplex()  
    {  
    }  
    public new void ShowXY()  
    {  
        Console.WriteLine("{0} {1}", x, y);  
    }  
}  
class MyClient  
{  
    public static void Main()  
    {  
        MyComplex mc1 = new MyComplex(1.5, 2.5);  
        mc1.ShowXY();  
        MyComplex mc2 = new MyComplex(3.5, 4.5);  
        mc2.ShowXY();  
        MyComplex mc3 = new MyComplex();  
        //mc3 = mc1 + mc2;  
        //mc3.ShowXY();  
    }  
}

Equality Operator Overloading

You may already be acquainted with user-defined classes that automatically inherit the Syste.object.Equals method from the Syste.object in C#. The Equals method facilitates reference-based comparison. However, it's important to note that this method can potentially override user-defined class methods. As a result, value-based comparison becomes accessible through this method. This is the mechanism through which the Equality operator functions. To customize this operator, adhere to the code snippet provided below.

Example

using System;  
class Complex  
{  
    private int x;  
    private int y;  
    public Complex()  
    {  
    }  
    public Complex(int i, int j)  
    {  
        x = i;  
        y = j;  
    }  
    public void ShowXY()  
    {  
        Console.WriteLine("{0} {1}", x, y);  
    }  
}  
class MyClient  
{  
    public static void Main()  
    {  
        Complex c1 = new Complex(10, 20);  
        c1.ShowXY(); // displays 10 & 20  
        Complex c2 = new Complex(10, 20);  
        c2.ShowXY(); // displays 10 & 20  
        Complex c3 = c2;  
        c3.ShowXY(); // dislplays 10 & 20  
        if (c1.Equals(c2))  
            Console.WriteLine("OK");  
        else  
            Console.WriteLine("NOT OK");  
        if (c2.Equals(c3))  
            Console.WriteLine("OK1");  
    }  
}

In the given program, the output appears as "NOT OJ" and "OK1". This typically indicates that the Equals method is conducting a reference comparison by default. It's crucial to understand that while the content of objects C2 and C1 is identical in the provided code, they are stored in different memory locations. However, a closer observation reveals that C2 and C3 point to the same memory address. This exemplifies how the Equals method handles equality operator overloading. In C#, it is feasible to customize the Equals method within any user-defined class to enable value-based comparisons. Below is a code snippet illustrating this concept.

Example

using System;  
class Complex  
{  
    private int x;  
    private int y;  
    public Complex()  
    {  
    }  
    public Complex(int i, int j)  
    {  
        x = i;  
        y = j;  
    }  
    public void ShowXY()  
    {  
        Console.WriteLine("{0} {1}", x, y);  
    }  
    public override bool Equals(object o)  
    {  
        if ((Complex)o.x == this.x && (Complex)o.y == this.y)  
            return true;  
        else  
            return false;  
    }  
    public override int GetHashCode()  
    {  
        return this.ToString().GetHashCode();  
    }  
}  
class MyClient  
{  
    public static void Main()  
    {  
        Complex c1 = new Complex(10, 20);  
        c1.ShowXY(); // displays 10 & 20  
        Complex c2 = new Complex(10, 20);  
        c2.ShowXY(); // displays 10 & 20  
        Complex c3 = c2;  
        c3.ShowXY(); // dislplays 10 & 20  
        if (c1.Equals(c2))  
            Console.WriteLine("OK");  
        else  
            Console.WriteLine("NOT OK");  
        if (c2.Equals(c3))  
            Console.WriteLine("OK1");  
    }  
}

Summary

Throughout this guide, you have explored the fundamental techniques commonly employed for incorporating operator overloading in C#. You have encountered scenarios where operator overloading is achieved through unary, binary, and equality operators. It should be evident to you now that certain operators impose constraints on the program's execution flow, necessitating a clear grasp of where these conditions are applied. Furthermore, when engaging in operator overloading in C#, it is essential to delve into the intricacies of how data flows during implementation and how a single data type can yield diverse outputs.

Input Required

This code uses input(). Please provide values below: