Operators can be overloaded to provide more natural syntax for custom types. Operator overloading is most appropriately used for implementing custom structs that represent fairly primitive data types. For example, a custom numeric type is an excellent candidate for operator overloading.
The overloadable symbolic operators are as follows:
+ (unary) - (unary) ! ~ ++ -- + - * / % & | ^ << >> == != > < >= <=
The following operators are also overloadable:
Implicit and explicit conversions (with the implicit
and explicit
keywords)
The literals true
and false
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 |.
An operator is overloaded by declaring an operator function. An operator function has the following rules:
The name of the function is specified with the operator
keyword followed by an operator symbol.
The operator function must be marked static
.
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.
In the following example, we define a struct called Note
representing a musical note, and then overload the + operator:
public struct Note
{
int value;
public Note (int semitonesFromA)
{ value = semitonesFromA; }
public static Note operator + (Note x, int semitones)
{
return new Note (x.value + semitones);
}
}
This overload allows us to add an int
to a Note
:
Note B = new Note(2); Note CSharp = B + 2;
Overloading an assignment operator automatically supports the corresponding compound assignment operator. In our example, because we overrode +, we can use += too:
CSharp += 2;
Equality and comparison operators are sometimes overridden when writing structs, and in rare cases when writing classes. Special rules and obligations come with overloading the equality and comparison operators:
The C# compiler enforces that operators that are logical pairs are both defined.
These operators are (== !=
), (< >
), and (<=
>=
).
Equals
and
GetHashCode
If you overload ==
and !=
, you will usually need to override object
’s Equals
and
GetHashCode
methods so that collections and
hashtables will work reliably with the type.
IComparable
and
IComparable<T>
If you overload (< >
) and (<= >=
), you would also typically implement
IComparable
and IComparable<T>
.
Extending the previous example, here’s how we could overload Note
’s equality operators:
public static bool operator == (Note n1, Note n2) { return n1.value == n2.value; } public static bool operator != (Note n1, Note n2) { return !(n1.value == n2.value); } public override bool Equals (object otherNote) { if (!(otherNote is Note)) return false; return this == (Note)otherNote; } public override int GetHashCode( ) { return value.GetHashCode( ); // Use value's hashcode }
Implicit and explicit conversions are overloadable operators. These conversions are typically overloaded to make converting between strongly related types (such as numeric types) concise and natural.
To convert between weakly related types, the following strategies are more suitable:
Write a constructor that has a parameter of the type to convert from.
Write To
XXX
and
From
XXX
methods to
convert between types.
As explained in the discussion on types, the rationale behind implicit conversions is that they are guaranteed to succeed and do not lose information during the conversion. Conversely, an explicit conversion should be required either when runtime circumstances will determine whether the conversion will succeed or if information may be lost during the conversion.
In this example, we define conversions between our musical Note
type and a double (which represents the frequency in hertz of that
note):
... // Convert to hertz public static implicit operator double (Note x) { return 440 * Math.Pow (2,(double) x.value / 12 ); } // Convert from hertz (accurate to nearest semitone) public static explicit operator Note (double x) { return new Note ((int) (0.5 + 12 * (Math.Log(x/440) / Math.Log(2)) )); } ... Note n =(Note)554.37; // explicit conversion double x = n; // implicit conversion