
Understanding Operators
You use operators to combine operands together into expressions. Each operator has its
own semantics dependent on the type it works with. For example, the + operator means
“add” when used with numeric types, or “concatenate” when used with strings.
Each operator symbol has a precedence. For example, the * operator has a higher
precedence than the + operator. This means that the expression a + b * c is the same as a
+ (b * c).
Each operator symbol also has an associativity to define whether the operator evaluates
from left to right or from right to left. For example, the = operator is right-associative (it
evaluates from right to left), so a = b = c is the same as a = (b = c).
NOTE
The right-associativity of the = operator allows you to perform multiple assignments in
the same statement. For example, you can initialize several variables to the same value
like this:
int a, b, c, d, e;
a = b = c = d = e = 99;
The expression e = 99 is evaluated first. The result of the expression is the value that was
assigned (99), which is then assigned to d, c, b, and finally a in that order.
A unary operator is an operator that has just one operand. For example, the increment
operator (++) is a unary operator.
A binary operator is an operator that has two operands. For example, the multiplication
operator (*) is a binary operator.
Operator Constraints
C# allows you to overload most of the existing operator symbols for your own types.
When you do this, the operators you implement automatically fall into a well-defined
framework with the following rules:
• You cannot change the precedence and associativity of an operator. The
precedence and associativity are based on the operator symbol (for example, +)
and not on the type (for example, int) on which the operator symbol is being used.
Hence, the expression a + b * c is always the same as a + (b * c), regardless of the
type of a, b, and c.

• You cannot change the multiplicity of an operator (the number of operands). For
example, * (the symbol for multiplication), is a binary operator. If you declare a *
operator for your own type, it must be a binary operator.
• You cannot invent new operator symbols. For example, you can't create a new
operator symbol, such as ** for raising one number to the power of another
number. You'd have to create a method for that.
• You can't change the meaning of operators when applied to built-in types. For
example, the expression 1 + 2 has a predefined meaning and you're not allowed to
override this meaning. If you could do this, things would be too complicated!
• There are some operator symbols that you can't overload. For example, you can't
overload the dot operator (member access). Again, if you could do this, it would
lead to unnecessary complexity.
TIP
You can use indexers to simulate [ ] as an operator. Similarly, you can use
properties to simulate = (assignment) as an operator, and you can use delegates to
simulate a function call as an operator.
Overloaded Operators
To define your own operator behavior, you must overload a selected operator. You use
method-like syntax with a return type and parameters, but the name of the method is the
keyword operator together with the operator symbol you are declaring. For example,
here's a user-defined struct called Hour that defines a binary + operator to add together
two instances of Hour:
struct Hour
{
public Hour(int initialValue)
{
this.value = initialValue;
}
public static Hour operator+ (Hour lhs, Hour rhs)
{
return new Hour(lhs.value + rhs.value);
}
...
private int value;
}
Notice the following:

• The operator is public. All operators must be public.
• The operator is static. All operators must be static. Operators are never
polymorphic, and cannot use the virtual, abstract, override, or sealed modifiers.
• A binary operator (such as + shown above) has two explicit arguments and a unary
operator has one explicit argument (C++ programmers should note that operators
never have a hidden this parameter).
TIP
When declaring highly stylized functionality (such as operators), it is useful to
adopt a naming convention for the parameters. For example, developers often use
lhs and rhs (acronyms for left-hand side and right-hand side, respectively) for
binary operators.
When you use the + operator on two expressions of type Hour, the C# compiler
automatically converts your code into a call to the user-defined operator. The C#
compiler converts this:
Hour Example(Hour a, Hour b)
{
return a + b;
}
Into this:
Hour Example(Hour a, Hour b)
{
return Hour.operator+(a,b); // pseudocode
}
Note, however, that this syntax is pseudocode and not valid C#. You can use a binary
operator only in its standard infix notation (with the symbol between the operands).
There is one final rule you must follow when declaring an operator otherwise your code
will not compile: At least one of the parameters should always be of the containing type.
In the previous operator+ example for the Hour class, one of the parameters, a or b, must
be an Hour object. In this example, both parameters are Hour objects. However, there
could be times when you want to define additional implementations of operator+ that add
an integer (a number of hours) to an Hour object—the first parameter could be Hour, and
the second parameter could be the integer. This rule makes it easier for the compiler to
know where to look when trying to resolve an operator invocation, and it also ensures
that you can't change the meaning of the built-in operators.
Creating Symmetric Operators

In the previous section, you saw how to declare a binary + operator to add together two
instances of type Hour. The Hour struct also has a constructor that creates an Hour from
an int. This means that you can add together an Hour and an int—you just have to first
use the Hour constructor to convert the int into an Hour. For example:
Hour a = ...;
int b = ...;
Hour sum = a + new Hour(b);
This is certainly valid code, but it is not as clear or as concise as adding together an Hour
and an int directly, like this:
Hour a = ...;
int b = ...;
Hour sum = a + b;
To make the expression (a + b) valid, you must specify what it means to add together an
Hour (a, on the left) and an int (b, on the right). In other words, you must declare a binary
+ operator whose first parameter is an Hour and whose second parameter is an int. The
following code shows the recommended approach:
struct Hour
{
public Hour(int initialValue)
{
this.value = initialValue;
}
...
public static Hour operator+ (Hour lhs, Hour rhs)
{
return new Hour(lhs.value + rhs.value);
}
public static Hour operator+ (Hour lhs, int rhs)
{
return lhs + new Hour(rhs);
}
...
private int value;
}
Notice that all the second version of the operator does is construct an Hour from its int
argument, and then call the first version. In this way, the real logic behind the operator is

held in a single place. The point is that the extra operator+ simply makes existing
functionality easier to use. Also, notice that you should not provide many different
versions of this operator, each with a different second parameter type—only cater for the
common and meaningful cases, and let the user of the class take any additional steps if an
unusual case is required.
This operator+ declares how to add together an Hour as the left-hand operand and an int
as the right-hand operator. It does not declare how to add together an int as the left-hand
operand and an Hour as the right-hand operand:
int a = ...;
Hour b = ...;
Hour sum = a + b; // compile-time error
This is counter-intuitive. If you can write the expression a + b, you expect to also be able
to write b + a. Therefore, you should provide another overload of operator+:
struct Hour
{
public Hour(int initialValue)
{
this.value = initialValue;
}
...
public static Hour operator+ (Hour lhs, int rhs)
{
return lhs + new Hour(rhs);
}
public static Hour operator+ (int lhs, Hour rhs)
{
return new Hour(lhs) + rhs;
}
...
private int value;
}
NOTE
C++ programmers should notice that you must provide the overload yourself. The
compiler won't write it for you or silently swap the sequence of the two operands to find a
matching operator.
Operators and the Common Language Specification

