# Operators

C# provides an extensive set of operators for built-in types. Operators are broadly classified in the following categories: arithmetic, relational, logical, bitwise, assignment, and other operators. Some operators can be overloaded for user-defined types. This topic will be further discussed in *Chapter 5*, *Object-Oriented Programming in C#*.

When evaluating an expression, operator precedence and associativity determine the order in which the operations are performed. You can change this order by using *parentheses*, just like you would do with a mathematical expression.

The following table lists the order of the operators with the highest precedence at the top and the lowest at the bottom. Operators that are listed together, on the same row, have equal precedence:

For operators with the same precedence, associativity determines which one is evaluated first. There are two types of associativity:

**Left-associativity**: This determines operators to be evaluated from*left to right*. All of the binary operators are left-associative except for the assignment operators and the null coalescing operators.**Right-associativity**: This determines operators to be evaluated from*right to left*. The assignment operator, the null-coalescing operator, and the conditional operator are right-associative.

In the following sections, we will take a closer look at each category of operators.

## Arithmetic operators

**Arithmetic operators** perform arithmetic operations on the numerical type and can be unary or binary operators. A unary operator has a single operand, and a binary operator has two operands. The following set of arithmetic operators are defined in C#:

`+`

, `-`

, and `*`

will work as per the mathematical rules of addition, subtraction, and multiplication respectively. However, the `/`

operator behaves a bit differently. When applied to an integer, it will truncate the remainder of the division. For example, 20/3 will return 6. To get the remainder, we need to use the modulus operator. For example, 20%3 will return 2.

Among these, the increment and decrement operators require special attention. These operators have two forms:

- A postfix form
- A prefix form

The increment operator will increase the value of its operand by `1`

, whereas the decrement operator will decrease the value of its operand by `1`

. In the following example, the `a`

variable is initially `10`

, but after applying the increment operator, its value will be `11`

:

int a = 10; a++;

The prefix and the postfix variants differ in the following way:

- The prefix operator first performs the operation and then returns the value.
- The postfix operator first retains the value, then increments it, and then returns the original value.

Let's understand this with the help of the following code snippet. In the following example, `a`

is `10`

. When `a++`

is assigned to `b`

, `b`

takes the value `10`

and `a`

is incremented to `11`

:

int a = 10; int b = a++;

However, if we change this so that we assign `++a`

to `b`

, then `a`

will be incremented to `11`

, and that value will be assigned to `b`

, so both `a`

and `b`

will have the value `11`

:

int a = 10; int b = ++a;

The next category of operators that we will learn about is the relational operator.

## Relational operators

Relational operators, also called **comparison operators**, perform a comparison on their operands. C# defines the following sets of relational operators:

The result of a relational operator is a `bool`

value. These operators support all of the built-in numerical and floating-point types. However, enumerations also support these operators. For operands of the same enumeration type, the corresponding values of the underlying integral types are compared. Enumerations will be later discussed in *Chapter 4*, *Understanding the Various User-Defined Types*.

The next code listing shows several relational operators being used:

int a = 42; int b = 10; bool v1 = a != b; bool v2 = 0 <= a && a <= 100; if(a == 42) { /* ... */ }

The `<`

, `>`

, `<=`

, and `>=`

operators can be overloaded for user-defined types. However, if a type overloads `<`

or `>`

, it must overload both of them. Similarly, if a type overloads `<= `

or `>=`

, it must overload both of them.

## Logical operators

Logical operators perform a logical operation on `bool`

operands. The following set of logical operators are defined in C#:

The following example shows these operands in use:

bool a = true, b = false; bool c = a && b; bool d = a || !b;

In this example, since `a`

is `true`

and `b`

is `false`

, `c`

will be `false`

and `d`

will be `true`

.

## Bitwise and shift operators

A bitwise operator will work directly on the bits of their operands. A bitwise operator can only be used with integer operands. The following table lists all of the bitwise and shift operators:

In the following example, `a`

is `10`

, which in binary is `1010`

, and `b`

is `5`

, which in binary is `0101`

. The result of the bitwise AND is `0000`

, so `c`

will have the value `0`

, and the result of bitwise OR is `1111`

, so `d`

will have the value `15`

:

int a = 10; // 1010 int b = 5; // 0101 int c = a & b; // 0000 int d = a | b; // 1111

The left-shift operator shifts the left-hand operand to the left by the number of bits defined by the right-hand operand. Similarly, the right-shift operator shifts the left-hand operand to the right by the number of bits defined by the right-hand operand. The left-shift operator discards the higher-order bits that are outside the range of the result type and sets the lower-order bits to zero. The right-shift operator discards the lower-order bits and the higher-order bits are set as follows:

- If the value that is shifted is
`int`

or`long`

, an arithmetic shift is performed. That means the sign bit is propagated to the right on the higher-order empty bits. As a result, for a positive number, the higher-order bits are set to zero (because the sign bit is*0*) and for a negative number, the higher-order bits are set to one (because the sign bit is*1*). - If the value that is shifted is
`uint`

or`ulong`

, a logical shift is performed. In this case, the higher-order bits are always set to`0`

.

The shift operations are only defined for `int`

, `uint`

, `long`

, and `ulong`

. If the left-hand operand is of another integral type, it is converted to `int`

before the operation is applied. The result of a shift operation will always contain at least 32 bits.

The following listing shows examples of shifting operations:

// left-shifting int x = 0b_0000_0110; x = x << 4; // 0b_0110_0000 uint y = 0b_1111_0000_0000_0000_1111_1110_1100_1000; y = y << 2; // 0b_1100_0000_0000_0011_1111_1011_0010_0000; // right-shifting int x = 0b_0000_0000; x = x >> 4; // 0b_0110_0000 uint y = 0b_1111_0000_0000_0000_1111_1110_1100_1000; y = y >> 2; // 0b_0011_1100_0000_0000_0011_1111_1011_0010;

In this example, we initialized the `x`

and `y`

variables with binary literals to make it easier to understand how shifting works. The value of the variables after shifting is also shown in binary in the comments.

## Assignment operators

An assignment operator assigns a value to its left operand based on the value of its right operand. The following assignment operators are available in C#:

In this table, we have the simple assignment operator (`=`

) that assigns the right-hand value to the left operand, and then we have compound assignment operators, that first perform an operation (arithmetical, shifting, or bitwise) and then assign the result of the operation to the left operand. Therefore, operations such as `a = a + 2`

and `a += 2`

are equivalent.

## Other operators

Apart from the operators discussed so far, there are other useful operators in C# that work both on built-in types and user-defined types. These include the conditional operator, the null-conditional operators, the null-coalescing operator, and the null-coalescing assignment operator. We will look at these operators in the following pages.

The ternary conditional operator

The **ternary conditional operator** is denoted by `?:`

and often simply referred to as the *conditional operator*. It allows you to return a value from two available options based on whether a Boolean condition evaluates to `true`

or `false`

.

The syntax of the ternary operator is as follow:

condition ? consequent : alternative;

If the Boolean condition evaluates to `true`

, the `consequent`

expression will be evaluated, and its result returned. Otherwise, the `alternative`

expression will be evaluated, and its result returned. The ternary conditional operator can also be perceived as a shorthand for an `if-else`

statement.

In the following example, the function called `max()`

returns the maximum of two integers. The conditional operator is used to check whether `a`

is greater or equal to `b`

, in which case the value of `a`

is returned; otherwise, the result is the value of `b`

:

static int max(int a, int b) { return a >= b ? a : b; }

There is another form of this operator called **conditional ref expression** (available since C# 7.2) that allows returning the reference to the result of one of the two expressions. The syntax, in this case, is as follows:

condition ? ref consequent : ref alternative;

The result reference can be assigned to a `ref`

local or `ref`

read-only local variable and uses it as a reference return value or as a ref method parameter. The conditional `ref`

expression requires the type of `consequent`

and `alternative`

to be the same.

In the following example, the conditional `ref`

expression is used to select between two alternatives based on user input. If an even number is introduced, the `v`

variable will hold a reference to `a`

; otherwise, it will hold a reference to `b`

. The value of `v`

is incremented and then `a`

and `b`

are printed to the console:

int a = 42; int b = 21; int.TryParse(Console.ReadLine(), out int alt); ref int v = ref (alt % 2 == 0 ? ref a : ref b); v++; Console.WriteLine($"a={a}, b={b}");

While the conditional operator checks whether a condition is true or not, the null-conditional operator checks whether an operand is null or not. We will look at this operator in the next section.

### The null-conditional operators

The **null-conditional operator** has two forms: `?.`

(also known as the **Elvis operator**) to apply member access and `?[]`

to apply element access for an array. These operators apply the operation to their operand if and only if that operand is not `null`

. Otherwise, the result of applying the operator is also `null`

.

The following example shows how to use the null-conditional operator to invoke a method called `run()`

from an instance of a class called `foo`

, through an object that might be `null`

. Notice that the result is a nullable type (`int?`

) because if the operand of `?.`

is `null`

, then the result of its evaluation is also `null`

:

class foo { public int run() { return 42; } } foo f = null; int? i = f?.run()

The null-conditional operators can be *chained* together. However, if one operator in the chain is evaluated to `null`

, the rest of the chain is *short-circuited* and does not evaluate.

In the following example, the `bar`

class has a property of the `foo`

type. An array of `bar`

objects is created and we try to retrieve the value from the execution of the `run()`

method from the `f`

property of the first `bar`

element in the array:

class bar { public foo f { get; set; } } bar[] bars = new bar[] { null }; int? i = bars[0]?.f?.run();

We can avoid the use of a nullable type if we combine the null-conditional operator with the null-coalescing operator and provide a default value in case the null-conditional operator returns `null`

. An example is shown here:

int i = bars[0]?.f?.run() ?? -1;

The null-coalescing operator is discussed in the following section.

### The null-coalescing and null-coalescing assignment operators

The **null-coalescing operator**, denoted by `??`

, will return the left-hand operand if it is not `null`

; otherwise, it will evaluate the right-hand operand and return its result. The left-hand operand cannot be a non-nullable value type. The right-hand operand is only evaluated if the left-hand operand is `null`

.

The **null-coalescing assignment operator**, denoted by `??=`

, is a new operator added in C# 8. It assigns the value of its right-hand operand to its left-hand operand, if and only if the left-hand operand evaluates to `null`

. If the left-hand operand is not `null`

, then the right-hand operand is not evaluated.

Both `??`

and `??=`

are *right-associative*. That means, the expression `a ?? b ?? c`

is evaluated as `a ?? (b ?? c)`

. Similarly, the expression `a ??= b ??= c`

is evaluated as `a ??= (b ??= c)`

.

Take a look at the following code snippet:

int? n1 = null; int n2 = n1 ?? 2; // n2 is set to 2 n1 = 5; int n3 = n1 ?? 2; // n3 is set to 5

We have defined a nullable variable, `n1`

, and initialized it to `null`

. The value of `n2`

will be set to `2`

as `n1`

is `null`

. After assigning `n1`

a non-null value, we will apply the conditional operator on `n1`

and integer `2`

. In this case, since `n1`

is not `null`

, the value of `n3`

will be the same as that of `n1`

.

The null-coalescing operator can be used multiple times in an expression. In the following example, the `GetDisplayName()`

function returns the value of `name`

if this is not `null`

; otherwise, it returns the value of `email`

if it is not `null`

; if `email`

is also `null`

, then it returns `"unknown"`

:

string GetDisplayName(string name, string email) { return name ?? email ?? "unknown"; }

The null-coalescing operator can also be used in argument checking. If a parameter is expected to be non-null, but it is in fact `null`

, you can throw an exception from the right-hand operand. This is shown in the following example:

class foo { readonly string text; public foo(string value) { text = value ?? throw new ArgumentNullException(nameof(value)); } }

The null-coalescing assignment operator is useful in replacing code that checks whether a variable is `null`

before assigning it with a simpler, more succinct form. Basically, the `??=`

operator is syntactic sugar for the following code:

if(a is null) a = b;

This can be replaced with `a ??= b`

.