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"); }