C# Basic Operator Overloading

Operator Overloading
So far in this tutorial we have created classes to represent real-world objects complete with their appropriate methods and properties. These objects have not required the implementation of arithmetic operators as this type of functionality was not appropriate. In this article, we will create a class that does support arithmetic operations through the use of operator overloading.
Operator overloading is simply the process of adding operator functionality to a class. This allows you to define exactly how the operator behaves when used with your class and other data types. This can be standard uses such as the ability to add the values of two vectors, more complex mathematics for multiplying matrices or non-arithmetic functions such as using the + operator to add a new item to a collection or combine the contents of two arrays. Multiple overloaded versions of operators may also be created to provide different functionality according to the data types being processed, in a similar manner to the varying signatures of method overloading.
In this article we will create a new class to represent a two-dimensional vector with X and Y properties. We will use this class in this article and future articles to demonstrate operator overloading. To start, create a new console application named “VectorDemo” and add a new class file named “Vector”. Add the following code to the new class to create the properties and a basic constructor:

private int _x, _y;
public Vector(int x, int y) { _x = x; _y = y; }
public int X
{
get { return _x; }
set { _x = value; }
}
public int Y
{
get { return _y; }
set { _y = value; }
}

Binary Operator Overloading
The first type of operator to consider is the binary operator, so named because they require two values to work with. These include the simple arithmetic operators such as +, -, *, / and %. To declare a binary operator, the following syntax is used:

public static result-type operator binary-operator (op-type operand,op-type2 operand2)

This initially appears to be a rather complex declaration but in fact is quite simple. The declaration starts with public static as all operators must be declared as such. Other scopes are not permitted and neither are non-static operators.
The result-type defines the data type or class that is returned as the result of using the operator. Usually this will be the same type as the class that it is being defined within. However, that need not be the case and it is perfectly valid to return data of a different type.
The operator keyword is added to tell the compiler that the following binary-operator symbol is an operator rather than a normal method. This operator will then process the two operand parameters, each prefixed with its data type (op-type and op-type2). As least one of these operands must be the same type as the containing class.
Creating the Addition (+) Operator
The syntax for binary operators can now be used to create a new addition operator for the Vector class. This operator will simply add the X and Y elements of two vectors together and return a new vector containing the result. Add the following to the Vector class to provide this functionality. Note that a new Vector is created rather than adjusting one of the operands. This is because the operands are reference-types and the original values should not be updated in this case.

public static Vector operator +(Vector v1, Vector v2)
{
return new Vector(v1.X + v2.X, v1.Y + v2.Y);
}

We can now test the Vector’s new operator by modifying the program’s main method. The following program instantiates two Vector objects, adds them together and outputs the values of the resultant Vector’s X and Y properties.

static void Main(string[] args)
{
Vector v1 = new Vector(4, 11);
Vector v2 = new Vector(0, 8);
Vector v3 = v1 + v2;
Console.WriteLine("({0},{1})", v3.X, v3.Y); // Outputs "(4,19)"
}

Creating the Subtraction (-) Operator
Addition is a commutative operation. This means the order of the two operands can be swapped without affecting the outcome. In the case of subtraction this is not the case so it important to remember that the first operand in the declaration represents the value to the left of the operator and the second operand represents the value to the right. If these are used incorrectly, the resultant value will be incorrect. Using this knowledge we can add a subtraction operator to the Vector class:

public static Vector operator -(Vector v1, Vector v2)
{
return new Vector(v1.X - v2.X, v1.Y - v2.Y);
}

To test the new operator, modify the Main method as follows and execute the program.

static void Main(string[] args)
{
Vector v1 = new Vector(4, 11);
Vector v2 = new Vector(0, 8);
Vector v3 = v1 - v2;
Console.WriteLine("({0},{1})", v3.X, v3.Y); // Outputs "(4,3)"
}

Creating the Multiplication (*) Operator
The last binary operator that will be added to Vector class is for multiplication. This operator will be used to scale the vector by multiplying the X and Y properties by the same integer value. This demonstrates the use of operands of a different type to the class they are defined within.

public static Vector operator *(Vector v1, int scale)
{
return new Vector(v1.X * scale, v1.Y * scale);
}

To test the multiplication operator, adjust the Main method again:

static void Main(string[] args)
{
Vector v1 = new Vector(4, 11);
Vector v2 = v1 * 3;
Console.WriteLine("({0},{1})", v2.X, v2.Y); // Outputs "(12,33)"
}

In the operator code for the multiplication operator, the Vector is the first operand and the integer the second. This means that the order used in the multiplication statement must have the Vector at the left of the operator and the integer value to the right. Changing the order of the operands in the Main method will cause a compiler error.

static void Main(string[] args)
{
Vector v1 = new Vector(4, 11);
Vector v2 = 3 * v1;
Console.WriteLine("({0},{1})", v2.X, v2.Y); // Does not compile
}

If the class must support both variations of multiplication, both must be declared in the code. This provides the benefit of allowing the order of operands change the underlying function. To provide the second variation of multiplication, add the following code to the Vector class. Afterwards, the program will execute correctly.

public static Vector operator *(int scale, Vector v1)
{
return new Vector(v1.X * scale, v1.Y * scale);
}

Compound Assignment Operators
When an arithmetic binary operator is declared, the corresponding compound assignment operator is also made available to the class. This can be demonstrated by modifying the Main method of the program:

Vector v1 = new Vector(4, 11);
v1 *= 5;
Console.WriteLine("({0},{1})", v1.X, v1.Y); // Outputs "(20,55)"

Unary Operator Overloading
Unary operators are those that require a single operand. These include the simple increment (++) and decrement (–) operators. To declare a unary operator, the following syntax is used:

public static result-type operator unary-operator (op-type operand)

This syntax is almost identical to that used for binary operators. The difference is that only one operand is declared. The operand type must be the same as the class in which the operator is declared.
Creating the Increment and Decrement Operators
Using the syntax defined above, we can now add the increment and decrement operators to the Vector class.
Note that there is only a single definition for each. There is no way to differentiate between prefix and postfix versions of the operator so both provide the same underlying functionality.
To declare the two operators, add the following code to the Vector class. Each increments or decrements both the X and Y properties for Vector objects.

public static Vector operator ++(Vector v)
{
v.X++;
v.Y++;
return v;
}
public static Vector operator --(Vector v)
{
v.X--;
v.Y--;
return v;
}

To test these operators, update and execute the Main method:

static void Main(string[] args)
{
Vector v1 = new Vector(4, 11);
v1++;
Console.WriteLine("({0},{1})", v1.X, v1.Y); // Outputs "(5,12)"
v1--;
Console.WriteLine("({0},{1})", v1.X, v1.Y); // Outputs "(4,11)"
}

Creating the Negation Operator
The last arithmetic unary operator to be considered in this article is the negation operator. This is the unary version of subtraction used to identify a negative version of a value. We can add this operator using the following code:

public static Vector operator -(Vector v)
{
return new Vector(-v.X, -v.Y);
}

To test the negation operator, update the Main method and run the program.

static void Main(string[] args)
{
Vector v1 = new Vector(4, 11);
Vector v2 = -v1;
Console.WriteLine("({0},{1})", v2.X, v2.Y); // Outputs "(-4,-11)"
}

Tagged , , . Bookmark the permalink.

Leave a Reply