Friday, April 12, 2019

C# Operator Overloading

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.

No comments:

Post a Comment

No String Argument Constructor/Factory Method to Deserialize From String Value

  In this short article, we will cover in-depth the   JsonMappingException: no String-argument constructor/factory method to deserialize fro...