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