12
Let’s explain what operator overloading is all about with an
example of a class that represents a date. Would it not be great if we could
subtract two date objects and be returned an int representing the number of
days elapsing between the two dates. We would like to use the good old
subtraction operator – like we do when subtracting numbers. Also we would like
the > operator to compare two date
objects and tell us which one is larger. The + operator could add a number to a
date resulting in a new date object.
Thus, operator overloading lets us redefine the existing
operators so that they work with classes/objects we create like yyy. We have
not yet instructed C# on how to use the trusty old + operator with two yyy
objects. Though C# knows how to use the + to add two numbers, it does not know
how to add two yyy’s.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy c;
c = a + b ;
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
}
Compiler Error
a.cs(8,5): error CS0019: Operator ‘+’ cannot be applied to
operands of type ‘yyy’ and ‘yyy’
We have created a simple class yyy which has one instance
variable i which will distinguish different instances of yyy from each other.
The constructor with an int as a parameter initializes i. We have tried to add
two objects that look like yyy which does not go down well with C# and it
objects to it.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy c;
c = a + b ;
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public yyy operator + ( yyy x , yyy y)
{
}
}
Compiler Error
a.cs(18,12): error CS0558: User-defined operators
‘yyy.operator +(yyy, yyy)’ must be declared static and public
Error messages can be at times helpful. Our operator + is
public but not static. C# demands that all operator overloads be static.
a.cs
public class zzz
{
public static void Main() {
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy c;
c = a + b ;
System.Console.WriteLine(c.i);
}
}
public class yyy
{
public int i;
public yyy( int j) {
i = j;
}
public static yyy operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
yyy z = new yyy(x.i+y.i);
return z;
}
}
Output
operator + 10 5
15
The word operator as the name of a function, is legal and
the only way to overload operators. We follow this word with the operator we
want to overload and then the parameters we will call the operator with. + is a
binary operator and will need two yyy’s, one on its left and the other on its right. Then at beginning, we give the return value of the operator. In
our case we want a + to add two yyy’s and return a third yyy whose i will be
the sum of the individual i’s. Thus a+b will call the operator + with x being
equal to a and y to b. Thus x.i will have a value 10 and y.i, 5. We are
creating a new object z and in the constructor passing 15 i.e. 10 + 5. Thus the
i of z will be 15 which is being returned. a + b will now be replaced by the
object whose i has a value 15 and c will be equal to this object. Thus c.i will
be equal to 15.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy c;
c = a + b ;
System.Console.WriteLine(c.i);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static yyy operator = ( yyy x , yyy y)
{
}
public static yyy operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
yyy z = new yyy(x.i+y.i);
return z;
}
}
Compiler Error
a.cs(19,28): error CS1020: Overloadable binary operator
expected
The error message is telling us that we cannot overload the
assignment operator =. Every class gets a free assignment operator which does a
bitwise copy of the variables of the object from the left to the right.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy c = new yyy(2);
yyy d;
d = a + b + c ;
System.Console.WriteLine(d.i);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static yyy operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
yyy z = new yyy(x.i+y.i);
return z;
}
}
Output
Operator + 10 5
Operator + 15 2
17
The only change is d = a + b + c. C# gets easily confused
with complex statements so it does not read all of it. It sees two operators on
the same line. In this case, the same plus. An internal rule tells it to read
the plus left to right i.e. it will only see a + b. It will call the operator +
with x.i as 10 and y.i as 5 because a’s i is 10 and b’s i is 5. This will
create a temporary object like yyy whose i is 15, lets call it zz. The object z
is very different from zz. C# then evaluates zz + c. Thus x.i will display 15
and y.i will have the value of c.i i.e. 2. To support multiple invocations of
the operator on a single line, the code does not change.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
int d = a + b ;
System.Console.WriteLine(d);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static int operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
return x.i + y.i;
}
}
Output
operator + 10 5
15
C# does not and will never understand what addition of two
yyy’s is all about. It is in your hands to decide what the code accomplishes.
You decide whether the overloaded + returns a yyy or an int object. The class
yyy is your creation not C#’s. Hence you decide what addition means in the
context of a yyy class. In this case, we are returning an int unlike earlier
where we returned a yyy.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
int d = a + b ;
System.Console.WriteLine(d);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static int operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
return x.i + y.i;
}
public static yyy operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
yyy z = new yyy(x.i+y.i);
return z;
}
}
Compiler Error
a.cs(23,19): error CS0111: Class ‘yyy’ already defines a
member called ‘ op_Addition ‘ with the same parameter types
You cannot have two operator + overloads which only differ
in return types. Also the error messages change the name of the operator from +
to op_Addition. When we overloaded functions, the return type was not
considered part of the function signature. The same applies for operators.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy d = a + b + 20;
System.Console.WriteLine(d.i);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static yyy operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
yyy z = new yyy(x.i+y.i);
return z;
}
}
Compiler Error
a.cs(7,9): error CS0019: Operator ‘+’ cannot be applied to
operands of type ‘yyy’ and ‘int’
C# is now telling you that you can add two yyy’s but cannot
do the same, i.e. add a yyy and an int as we have not told C# how to do so.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
yyy d = a + b + 20;
System.Console.WriteLine(d.i);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static yyy operator + ( yyy x , yyy y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y.i);
yyy z = new yyy(x.i+y.i);
return z;
}
public static yyy operator + ( yyy x , int y)
{
System.Console.WriteLine(“operator + “ + x.i + “ “ + y);
yyy z = new yyy(x.i+y);
return z;
}
}
Output
Operator + 10 5
Operator + 15 20
35
Life is fun in the fast lane. As we could have a large
number of functions with the same name but differing number of parameters,
ditto for operators. a + b as usual calls the first operator +. This as usual
creates a temp object, say zz and now
C# reads the line as zz + 20. This matches the second operator + which now gets
called. You can have over a million plus operators with differing parameters if
you desire.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
if ( a > b )
System.Console.WriteLine(“true”);
else
System.Console.WriteLine(“false”);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static bool operator > ( yyy x , yyy y)
{
System.Console.WriteLine(“operator < “ + x.i + “ “ + y.i);
return x.i > y.i;
}
}
Compiler Error
a.cs(20,20): error CS0216: The operator ‘yyy.operator
>(yyy, yyy)’ requires a matching operator ‘<‘ to also be defined
C# is a romantic at heart and loves pairs. We tried to
overload the > operator and C# tells us that we have to also overload the
< operator. It makes sense as a user would want to know whether a yyy is
greater than or less than another yyy.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
yyy b = new yyy(5);
if ( a > b )
System.Console.WriteLine(“true”);
else
System.Console.WriteLine(“false”);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static bool operator > ( yyy x , yyy y)
{
System.Console.WriteLine(“operator > “ + x.i + “ “ + y.i);
return x.i > y.i;
}
public static bool operator < ( yyy x , yyy y)
{
System.Console.WriteLine(“operator < “ + x.i + “ “ + y.i);
return x.i < y.i;
}
}
Output
operator > 10 5
true
The operator < returns a bool as we would like to use it
as part of an if or a while. In this case we are using it as part of an if and
nothing stops us from using the < overloaded for ints and yyy’s to return a
bool. What code you write in an operator is entirely your decision. The <
operator looks like the + and can take different parameters. To overload the
!=, you also have to overload the ==.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
System.Console.WriteLine(a);
System.Console.WriteLine(a.ToString());
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
}
Output
yyy
yyy
In the first WriteLine function to be called, we are passing
the object a whereas the first parameter to WriteLine should be a string. Also
we do not get an error and we see yyy displayed. A longtime back we told you
that all classes finally derive from object. The class object has a function
ToString. Thus calling the ToString function off a produces the same output as
the above line. We have not created the ToString function. So either we got a
free ToString implementation from C# like we get a free Constructor or the
ToString function of object in some way determines the name of our class and
returns it as a string.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
System.Console.WriteLine(a);
System.Console.WriteLine(a.ToString());
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Compiler Warning
a.cs(17,15): warning CS0114: ‘yyy.ToString()’ hides
inherited member ‘object.ToString()’. To make the current method override that
implementation, add the override keyword. Otherwise add the new keyword.
It is the same warning if a function exists in the base
class.
Output
yyy
ToString
mukhi
WriteLine displays yyy and a.ToString displays mukhi. Which
means that they call different ToString functions. The WriteLine(a) calls the
ToString of object whereas the second WriteLine calls it of yyy. If we want to
override a function in the base class, we have to specifically do so by using
the modifier override. If we do not, the default is to call the base class
ToString. All this has been explained earlier, in any case.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
System.Console.WriteLine(a);
System.Console.WriteLine(a.ToString());
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
ToString
mukhi
ToString
mukhi
Now both the System.Console.WriteLine calls the ToString
function of yyy. We would like to convert a yyy into a string or an int for
example. These type conversions are a major part of operator overloading.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
System.Console.WriteLine(a);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static implicit operator string(yyy y)
{
System.Console.WriteLine(“operator string”);
return “string “ + y.i;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
operator string
string 10
In spite of having a function ToString, it does not get
called unlike earlier. After the keyword operator we have the name of a data
type i.e. string. This function is called whenever we want to convert our
object into a string. The parameter y stands for the object to be converted. In
this function we can write whatever code we want to but we must return a
string. In our case we return the text string concatenated with the current
value of i.
Once again, the WriteLine function requires a string class.
We are offering a yyy class. C# checks whether there is an operator string
which is available to convert a yyy into a string. As there is one, it gets
called. The word implicit means that we are indirectly implying that it should
be called. Lets explain this with another example.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
string s;
s = a;
System.Console.WriteLine(s);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static explicit operator string(yyy y)
{
System.Console.WriteLine(“operator string”);
return “string “ + y.i;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Compiler Error
a.cs(7,5): error CS0029: Cannot implicitly convert type
‘yyy’ to ‘string’
We have changed the word implicit which means imply. It also
means giving a hint with the word explicit which means specify. Had we not
changed implicit with explicit, we would have got no error and C# would have
called the operator to convert a into a string and initialize s to it.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
string s;
s = (string)a;
System.Console.WriteLine(s);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static explicit operator string(yyy y)
{
System.Console.WriteLine(“operator string”);
return “string “ + y.i;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
operator string
string 10
The () is called a cast and it takes any data type within
brackets. We are explicitly asking for a conversion to a string and unlike
earlier we are not being implicit but explicit.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
string s;
s = (string)a;
System.Console.WriteLine(s);
System.Console.WriteLine(a);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static explicit operator string(yyy y)
{
System.Console.WriteLine(“operator string”);
return “string “ + y.i;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
operator string
string 10
ToString
mukhi
The explicit will get called only when we cast and as we do
not have an implicit modifier, the ToString gets called. We get no error if we
do not have an implicit modifier.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy(10);
string s;
s = (string)a;
System.Console.WriteLine(s);
System.Console.WriteLine(a);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public static explicit operator string(yyy y)
{
System.Console.WriteLine(“operator string”);
return “string “ + y.i;
}
public static implicit operator string(yyy y)
{
System.Console.WriteLine(“operator string”);
return “string “ + y.i;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Compiler Error
a.cs(24,15): error CS0557: Duplicate user-defined conversion
in class ‘yyy’
We cannot have both the implicit and explicit modifier as
two separate functions. Thus you have to decide which one you would want to
implement.
a.cs
public class zzz
{
public static void Main()
{
yyy a ;
a = 10;
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
a.cs(7,5): error CS0029: Cannot implicitly convert type
‘int’ to ‘yyy’
One more error. Here we are trying to equate a yyy to an int
but unlike earlier, we have not created an object like yyy by saying new. We
would need someone to create the yyy object and initialize it to 10, an int.
a.cs
public class zzz
{
public static void Main()
{
yyy a ;
a = 10;
System.Console.WriteLine(a.i);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
static public implicit operator yyy(int v)
{
System.Console.WriteLine(“operator yyy int “ + v);
yyy z = new yyy(v);
return z;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
operator yyy int 10
10
A constructor has the same name as the name of the class.
Thus operator yyy may double up as a constructor. When we write a = 10, C#
calls this constructor and passes the parameter v a value, 10. Operator yyy has
now to create an object that looks like yyy and return it. Thus it does not act
like a constructor in the sense that a constructor is responsible for creating the object. In other words, writing a
= 10, creates a new object that looks like yyy and initializes it to 10. It is
different from what we did earlier as previously the operator did not have to
create an object that looks like yyy. In this case it has to.
a.cs
public class zzz
{
public static void Main()
{
yyy a ;
a = “Hi”;
System.Console.WriteLine(a.i);
}
}
public class yyy
{
public int i;
public yyy( int j)
{
i = j;
}
static public implicit
operator yyy(int v)
{
System.Console.WriteLine(“operator yyy int “ + v);
yyy z = new yyy(v);
return z;
}
static public implicit
operator yyy(string v)
{
System.Console.WriteLine(“operator yyy string “ + v);
yyy z = new yyy(100);
return z;
}
public override string ToString()
{
System.Console.WriteLine(“ToString”);
return “mukhi”;
}
}
Output
operator yyy string Hi
100
We can have as many operator yyy functions as we like,
provided we follow the rules of function overloading. Thus we have two of them,
one that takes a string, another that takes an int. In this case as we are
writing a = “hi”, the second operator that accepts a string get called. They
all have to return an object that looks like yyy.
a = (short) 10;
Had we written the above line, we will not get any errors. We assumed we will get an error as we do not have an operator yyy which takes a short as a parameter. C# is highly intelligent. It first checks whether we have an operator yyy that matches what we wrote. In no case, there is a match. It will then check whether there is any other way it can prevent an error from being signaled. C# realizes that there is an operator yyy which accepts an int. Thus it converts a short to an int and then calls the operator yyy with the int parameter.