C# – Operator Overloading

Operator Overloading

Operators can be overloaded to provide more natural syntax for custom types.

The following operators can be overloaded:

+ (unary) – (unary) ! ~ ++
+ * /
% & | ^ <<
>> == != > <
>= <=

The following operators are also overloadable:

  • Implicit and explicit conversions
  • The true and false operators

The following operators are indirectly overloaded:

  • The compound assignment operators (e.g., +=, /=) are implicitly overridden by overriding the noncompound operators (e.g., +, /).
  • The conditional operators && and || are implicitly overridden by overriding the bitwise operators & and |.

Operator Functions

The operator is overloaded be declaring an operator function.

Operator function rules:

  • The name of the function is specified with the operator keyword followed by an operator symbol.
  • The operator function must be marked static and public.
  • The parameters of the operator function represent the operands.
  • The return type of an operator function represents the result of an expression.
  • At least one of the operands must be the type in which the operator function is declared.
//define struct Note representing a musical note and then overload the + operator.
public struct Note
{
	int value;
	public int SemitonesFromA => value;
	
	public Note (int semitonesFromA) { value = semitonesFromA; }

	public static Note operator + (Note x, int semitones)
	{
		return new Note (x.value + semitones);
	}
	
	// Or more tersely:
	// public static Note operator + (Note x, int semitones) => new Note (x.value + semitones);
	
	// See the last example in "Equality Comparison", Chapter 6 for an example of overloading the == operator
}

static void Main()
{
	//allows to add int to Note
        Note B = new Note (2);
	Note CSharp = B + 2;	
	CSharp.SemitonesFromA.Dump();
	
	//automatically overloads the corresponding compound assignment operator
        CSharp += 2;	
	CSharp.SemitonesFromA.Dump();
}

Overloading Equality and Comparison Operators

Equality and comparison operators are sometimes overridden when writing structs and classes.

Rules:
Pairing

The C# compiler enforces operators that are logical pairs to both be defined. These operators are (== notequal) (< >) (<= >=).

Equals and GetHashCode

When overloading (==) and (notequal) the Equals and GetHashCode methods defined in the object class needs to be overridden.

IComparable and IComparable<T>

If you overload (< >) and (<= >=), you should implement IComparable and IComparable<T>.

Custom Implicit and Explicit Conversions

Implicit and explicit conversions are overloadable operators.

The conversions are overloaded to make converting between strongly types concise and natural.

To convert weakly types:

Write a constructor that has a parameter of the type to convert from.

Write ToXXX and (static) FromXXX methods to convert between types.

// Implicit and explicit conversions are overloadable operators:

public struct Note
{
	int value;
	public int SemitonesFromA { get { return value; } }
	
	public Note (int semitonesFromA) { value = semitonesFromA; }

	// Convert to hertz
	public static implicit operator double (Note x) =>  440 * Math.Pow (2, (double) x.value / 12 );
	
	// Convert from hertz (accurate to the nearest semitone)
	public static explicit operator Note (double x) => new Note ((int) (0.5 + 12 * (Math.Log (x/440) / Math.Log(2) ) ));
}

static void Main()
{
	Note n = (Note)554.37;  // explicit conversion
	double x = n;           // implicit conversion
	x.Dump();	
}

Overloading true and false

// The true and false operators are overloaded in the extremely rare case of types that
// are boolean “in spirit”, but do not have a conversion to bool.

// An example is the System.Data.SqlTypes.SqlBoolean type which is defined as follows:

public struct SqlBoolean
{
	public static bool operator true (SqlBoolean x) => x.m_value == True.m_value;
	
	public static bool operator false (SqlBoolean x) => x.m_value == False.m_value;
	
	public static SqlBoolean operator ! (SqlBoolean x)
	{
		if (x.m_value == Null.m_value)  return Null;
		if (x.m_value == False.m_value) return True;
		return False;
	}
	
	public static readonly SqlBoolean Null =  new SqlBoolean(0);
	public static readonly SqlBoolean False = new SqlBoolean(1);
	public static readonly SqlBoolean True =  new SqlBoolean(2);
	
	SqlBoolean (byte value) { m_value = value; }
	byte m_value;
}


static void Main()
{
	SqlBoolean a = SqlBoolean.Null;
	if (a)
		Console.WriteLine ("True");
	else if (!a)
		Console.WriteLine ("False");
	else
		Console.WriteLine ("Null");	
}