C# Operator Overloading

In previous sections we learnt C# operators and C# Overloading, in this section, we will combine them together to introduce Operator Overloading.

Operator overloading is used to define or overload the built-in operators in a new user-defined class or struct. It looks like a special method with operator keyword and followed by the operator itself. See the following examples.

Example 01-81-01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;

struct Point
{
    int _x;
    int _y;

    public Point(int x, int y)
    {
        this._x = x;
        this._y = y;
    }

    public static Point operator +(Point a, Point b)
    {
        return new Point(a._x + b._x, a._y + b._y);
    }

    public static Point operator -(Point a, Point b)
    {
        return new Point(a._x + b._x, a._y + b._y);
    }

    public override string ToString()
    {
        return "(" + _x + "," + _y + ")";
    }
}

class Test
{
    static void Main()
    {
        Point p1 = new Point(1, 2);
        Point p2 = new Point(3, 4);

        Console.WriteLine("p1 + p2 = {0}", p1 + p2);
        Console.WriteLine("p1 - p2 = {0}", p1 - p2);

        Console.Read();
    }
}

Output

p1 + p2 = (4,6)
p1 - p2 = (-2,-2)

Explanation

  • Line 14: Define an operator +. 2 parameters indicate + is a binary operator and 2 Point type objects will be added together to return a new Point object.
  • Line 19: Define an operator -. It looks like a method.
  • Line 37-38: p1+p2 will be calculated in the defined operator + and p1-p2 will be calculated in the operator -.

Not all the operators can be overloaded. Please check the following.

NameOperatorsOverloadabilityComments
Unary+, -, !, ~, ++, --, true, falseYestrue and false are a pair and must be defined together.
Binary+, -, *, /, %, &, |, ^, <<, >>Yes2 parameters are needed.
Comparison==, !=, <, >, <=, >=YesOverloaded in pairs. == and != are a pair. So are < and >, <= and >=
Assignment+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=NoBut if + is overloaded the += operator is implicitly overloaded.
Others&&, ||, [], (), =, ., ?:, ->, new, is, sizeof, typeofNoexplicit and implicit can be used to defined new conversion operators.

The following rules must be followed when overloading an operator.

  • An user-defined operator must be declared static and public.
  • An user-defined operator implementation always take precedence over its predefined operator implementation.
  • If a binary operator is overloaded, the corresponding assignment operator is also implicitly overloaded.
  • Unary operators, like ++x or x--, can be overloaded as operator <operator>(x) with one parameter only.
  • Binary operators, like + or /, can be overloaded as operator <operator>(x, y) with 2 parameters.
  • The syntax, precedence or associativity of an operator can not be changed by any user-defined operator declarations.

Example 01-81-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using System;

class Student
{
    string _name;
    int _score;

    public Student(string name, int score)
    {
        this._name = name;
        this._score = score;
    }

    public static Student operator +(Student a, Student b)
    {
        return new Student(a._name + ", " + b._name, a._score + b._score);
    }

    public static Student operator +(Student a, int more)
    {
        return new Student(a._name, a._score + more);
    }

    public static Student operator +(int more, Student a)
    {
        return new Student(a._name, a._score + more);
    }

    public static Student operator ++(Student a)
    {
        return a + 1;
    }

    public static bool operator ==(Student a, Student b)
    {
        return a._score == b._score;
    }

    public static bool operator !=(Student a, Student b)
    {
        return a._score != b._score;
    }

    public override string ToString()
    {
        return "Name: " + _name + " Score: " + _score;
    }
}

class Test
{
    static void Main()
    {
        Student s1 = new Student("Mike", 70);
        Student s2 = new Student("Steve", 90);

        Console.WriteLine(s1);
        Console.WriteLine(s2);
        Console.WriteLine(s1 + s2);
        Console.WriteLine(s1 + 5);
        Console.WriteLine(2 + s1);
        Console.WriteLine(s1++);
        Console.WriteLine(s1);
        s1 += 7;
        Console.WriteLine(s1);

        Student s3 = new Student("Andy", 90);
        Console.WriteLine(s2 == s3 ? "s2 == s3" : "s2 != s3");
        Console.Read();
    }
}

Output

Name: Mike Score: 70
Name: Steve Score: 90
Name: Mike, Steve Score: 160
Name: Mike Score: 75
Name: Mike Score: 72
Name: Mike Score: 70
Name: Mike Score: 71
Name: Mike Score: 78
s2 == s3

Explanation

  • Line 14-27: Operator overloading. each of them has a different signature.
  • Line 29-32: The unary ++ operator is defined by calling another operator definition in line 19-22.
  • Line 34-42: == and != are redefined together because they are a pair. It is not allowed to define only one of them.
  • Line 59-61: + operator implementation is called based on the different parameters and their sequence.
  • Line 62-63: ++ operator implementation is called but the reaction of the postfix ++ has never been changed.
  • Line 64-65: += operator cannot be overloaded directly but it has been overloaded implicitly.
  • Line 67-68: Create a new Student object s3 with the same score in s2. We know both s2 and s3 are reference type and should not be equivalent by default. But the == operator is redefined and takes precedence over its predefined one.

Example 01-81-03

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;

class IsPositive
{
    int _value;
}

class Test
{
    static void Main()
    {
        IsPositive ip = new IsPositive();

        if (ip)     // illegal
        {
            Console.WriteLine("Never Printed");
        }
        
        Console.Read();
    }
}

You will get the following error message when trying to run the above example.

Cannot implicitly convert type 'IsPositive' to 'bool'

If it is in C++, the condition of the if statement will return true because the object is initialized and not null. But in C#, the condition must be returned as a boolean type. Thus, true/false operator must be redefined.

Example 01-81-04

The example 01-99-03 is changed as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;

class IsPositive
{
    int _value;

    public IsPositive(int value)
    {
        _value = value;
    }

    public static bool operator true(IsPositive x){
        return x._value > 0;
    }

    public static bool operator false(IsPositive x)
    {
        return x._value <= 0;
    }

    public static bool operator !(IsPositive x)
    {
        return x ? false : true ;
    }
}

class Test
{
    static void Main()
    {
        IsPositive ip = new IsPositive(-5);

        if (ip)     // Legal because true/false operators were defined.
        {
            Console.WriteLine("Positive Nubmer");
        }
        else
        {
            Console.WriteLine("Negative Nubmer");
        }

        if (!ip)     // Legal because true/false operators were defined.
        {
            Console.WriteLine("Negative Nubmer");
        }
        else
        {
            Console.WriteLine("Positive Nubmer");
        }

        Console.Read();
    }
}

Output

Negative Nubmer
Negative Nubmer

Explanation

  • Line 12-19: true and false user-defined Operators are defined as a pair.
  • Line 21-24: Define the ! operator. x is evaluated by calling true or false operator function.
  • Line 33: true and false user-defined Operator function is called.
  • Line 42: ! user-defined Operator function is called.

The () operator cannot be redefined directly. However, an explicit user-defined type conversion operator can be realized by the explicit keyword. also the implicit keyword can be used to declare an implicit user-defined type conversion operator.

Example 01-81-05

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;

class Number
{
    int _value;

    public Number(int value)
    {
        _value = value;
    }

    public static explicit operator Number(int x){
        return new Number(x);
    }

    public static implicit operator int(Number x)
    {
        return x._value;
    }

    public override string ToString()
    {
        return "Number:" + _value;
    }
}

class Test
{
    static void Main()
    {
        int i = 6;
        Number n = (Number)i;
        Console.WriteLine("{0}", n);

        int j = n;
        Console.WriteLine("j={0}", j);
        Console.Read();
    }
}

Output

Number:6
j=6

Explanation

  • Line 12-14: Explicit user-defined type conversion Operator is defined to convert an integer x to Number type by casting.
  • Line 16-19: Implicit user-defined type conversion Operator is defined to convert a Number type to an integer implicitly.
  • Line 31-32: Test the explicit operator by casting the integer i to a Number object.
  • Line 33: The override method ToString() defined in line 21-24 is called implicitly.
  • Line 35: Test the implicit operator by converting the Number object n to a new integer j implicitly.