C# Operators
Introduction to C# Operators
Operators in C# are symbols that perform operations on operands (variables or values). C# provides a rich set of operators that allow you to perform arithmetic, comparison, logical, bitwise, and other operations.
Arithmetic Operators
Arithmetic operators are used to perform common mathematical operations.
| Operator | Name | Description | Example | Result |
|---|---|---|---|---|
| + | Addition | Adds two operands | 5 + 3 |
8 |
| - | Subtraction | Subtracts right operand from left operand | 5 - 3 |
2 |
| * | Multiplication | Multiplies two operands | 5 * 3 |
15 |
| / | Division | Divides the left operand by the right operand | 15 / 3 |
5 |
| % | Modulus | Returns the remainder after division | 10 % 3 |
1 |
| ++ | Increment | Increases the value by 1 | int a = 5; a++; |
a becomes 6 |
| -- | Decrement | Decreases the value by 1 | int a = 5; a--; |
a becomes 4 |
Arithmetic Operators Example:
int a = 10;
int b = 3;
int sum = a + b; // 13
int difference = a - b; // 7
int product = a * b; // 30
int quotient = a / b; // 3 (integer division truncates decimal part)
int remainder = a % b; // 1
int c = 5;
int d = c++; // d = 5, c = 6 (postfix: assign then increment)
int e = 5;
int f = ++e; // f = 6, e = 6 (prefix: increment then assign)
Note that when using integer division, the result is always an integer, and any fractional part is truncated (not rounded). If you need to preserve the fractional part, use floating-point types.
The increment and decrement operators can be used in prefix form (++x, --x) or postfix form (x++, x--). In prefix form, the value is incremented or decremented before it's used in the expression, while in postfix form, the original value is used in the expression and then incremented or decremented.
Assignment Operators
Assignment operators are used to assign values to variables.
| Operator | Description | Example | Equivalent to |
|---|---|---|---|
| = | Simple assignment | x = 5 |
x = 5 |
| += | Add and assign | x += 3 |
x = x + 3 |
| -= | Subtract and assign | x -= 3 |
x = x - 3 |
| *= | Multiply and assign | x *= 3 |
x = x * 3 |
| /= | Divide and assign | x /= 3 |
x = x / 3 |
| %= | Modulus and assign | x %= 3 |
x = x % 3 |
| &= | Bitwise AND and assign | x &= 3 |
x = x & 3 |
| |= | Bitwise OR and assign | x |= 3 |
x = x | 3 |
| ^= | Bitwise XOR and assign | x ^= 3 |
x = x ^ 3 |
| <<= | Left shift and assign | x <<= 2 |
x = x << 2 |
| >>= | Right shift and assign | x >>= 2 |
x = x >> 2 |
Assignment Operators Example:
int x = 10;
x += 5; // x is now 15 (10 + 5)
x -= 3; // x is now 12 (15 - 3)
x *= 2; // x is now 24 (12 * 2)
x /= 4; // x is now 6 (24 / 4)
x %= 4; // x is now 2 (6 % 4)
// Compound assignment operators are more concise and often more efficient
// than their expanded forms
Comparison Operators
Comparison operators are used to compare two values. The result of a comparison is always a boolean value (true or false).
| Operator | Name | Description | Example | Result |
|---|---|---|---|---|
| == | Equal to | Checks if operands are equal | 5 == 3 |
false |
| != | Not equal to | Checks if operands are not equal | 5 != 3 |
true |
| > | Greater than | Checks if left operand is greater than right | 5 > 3 |
true |
| < | Less than | Checks if left operand is less than right | 5 < 3 |
false |
| >= | Greater than or equal to | Checks if left operand is greater than or equal to right | 5 >= 5 |
true |
| <= | Less than or equal to | Checks if left operand is less than or equal to right | 5 <= 3 |
false |
Comparison Operators Example:
int a = 5, b = 10;
bool isEqual = (a == b); // false
bool isNotEqual = (a != b); // true
bool isGreater = (a > b); // false
bool isLess = (a < b); // true
bool isGreaterOrEqual = (a >= b); // false
bool isLessOrEqual = (a <= b); // true
// Comparison operators are commonly used in conditional statements
if (a < b)
{
Console.WriteLine("a is less than b");
}
When comparing reference types (like objects, strings), the == operator compares references by default, not content. For content comparison of objects, you would typically use the Equals() method or override the == operator.
Logical Operators
Logical operators are used to determine the logic between variables or values.
| Operator | Name | Description | Example | Result |
|---|---|---|---|---|
| && | Logical AND | Returns true if both statements are true | true && false |
false |
| || | Logical OR | Returns true if one of the statements is true | true || false |
true |
| ! | Logical NOT | Reverses the result, returns false if the result is true | !true |
false |
Logical Operators Example:
bool isAdult = true;
bool hasLicense = false;
bool canDrive = isAdult && hasLicense; // false (both must be true)
bool isEligible = isAdult || hasLicense; // true (at least one is true)
bool isMinor = !isAdult; // false (reverse of isAdult)
// Logical operators are often used in conditional statements
if (isAdult && hasLicense)
{
Console.WriteLine("You can drive");
}
else
{
Console.WriteLine("You cannot drive");
}
The && and || operators in C# are short-circuit operators. This means that the second operand is only evaluated if necessary:
- For &&, if the first operand is false, the second operand is not evaluated (since the result will be false regardless).
- For ||, if the first operand is true, the second operand is not evaluated (since the result will be true regardless).
Bitwise Operators
Bitwise operators perform operations on binary representations of numeric values.
| Operator | Name | Description | Example | Result |
|---|---|---|---|---|
| & | Bitwise AND | Sets each bit to 1 if both bits are 1 | 5 & 3 |
1 |
| | | Bitwise OR | Sets each bit to 1 if any of the bits is 1 | 5 | 3 |
7 |
| ^ | Bitwise XOR | Sets each bit to 1 if only one of the bits is 1 | 5 ^ 3 |
6 |
| ~ | Bitwise NOT | Inverts all the bits | ~5 |
-6 |
| << | Left shift | Shifts bits left, filling with zeros from the right | 5 << 1 |
10 |
| >> | Right shift | Shifts bits right, filling with sign bit from the left | 5 >> 1 |
2 |
Bitwise Operators Example:
/* Understanding binary representation:
5 in binary is 0101
3 in binary is 0011 */
// Bitwise AND (&)
int result1 = 5 & 3; // 0101 & 0011 = 0001 (decimal: 1)
// Bitwise OR (|)
int result2 = 5 | 3; // 0101 | 0011 = 0111 (decimal: 7)
// Bitwise XOR (^)
int result3 = 5 ^ 3; // 0101 ^ 0011 = 0110 (decimal: 6)
// Bitwise NOT (~)
int result4 = ~5; // ~0101 = 1010 (with sign extension: decimal: -6)
// Left shift (<<)
int result5 = 5 << 1; // 0101 << 1 = 1010 (decimal: 10)
// Right shift (>>)
int result6 = 5 >> 1; // 0101 >> 1 = 0010 (decimal: 2)
Bitwise operators are often used for:
- Flag operations (setting, clearing, checking bits)
- Low-level optimization
- Working with hardware interfaces
- Efficient multiplication or division by powers of 2 (using shifts)
Ternary Operator
The ternary operator (? :) is a conditional operator that takes three operands. It's a shorthand for if-else statements.
Ternary Operator Syntax:
// Syntax: condition ? expression1 : expression2
// If condition is true, expression1 is evaluated and becomes the result
// If condition is false, expression2 is evaluated and becomes the result
int age = 20;
string message = (age >= 18) ? "Adult" : "Minor";
Console.WriteLine(message); // Output: "Adult"
// Equivalent if-else statement:
string message2;
if (age >= 18)
{
message2 = "Adult";
}
else
{
message2 = "Minor";
}
The ternary operator is useful for simple conditional assignments. It makes the code more concise but can reduce readability if overused or nested.
Null-related Operators
C# provides special operators for working with nullable types and null values.
| Operator | Name | Description | Example |
|---|---|---|---|
| ?? | Null-coalescing operator | Returns the left-hand operand if it isn't null; otherwise returns the right operand | result = value ?? defaultValue; |
| ?. | Null-conditional operator | Returns null if the operand is null; otherwise performs the operation | result = object?.Property; |
| ??= | Null-coalescing assignment | Assigns the right operand only if the left operand is null | target ??= defaultValue; |
Null-related Operators Example:
// Null-coalescing operator (??)
string name = null;
string displayName = name ?? "Guest"; // If name is null, use "Guest"
Console.WriteLine(displayName); // Output: "Guest"
// Null-conditional operator (?.)
Person person = null;
int? age = person?.Age; // No NullReferenceException, age will be null
Console.WriteLine(age); // Output: nothing (null)
// With actual object
Person john = new Person { Name = "John", Age = 30 };
int? johnsAge = john?.Age; // johnsAge will be 30
Console.WriteLine(johnsAge); // Output: 30
// Null-coalescing assignment (??=)
string message = null;
message ??= "Default message"; // Assigns only if message is null
Console.WriteLine(message); // Output: "Default message"
// Already has a value, won't change
message ??= "New message";
Console.WriteLine(message); // Output: "Default message" (unchanged)
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Null-related operators help write more concise and safer code when dealing with potentially null values. They help prevent NullReferenceExceptions and reduce the need for repetitive null checks.
Type Testing Operators
Type testing operators help determine the type of an object at runtime.
| Operator | Description | Example |
|---|---|---|
| is | Checks if an object is of a specific type | if (obj is string) |
| as | Converts an object to a specified type, returns null if conversion fails | string str = obj as string; |
| typeof | Gets the Type object for a type | Type stringType = typeof(string); |
Type Testing Operators Example:
// is operator
object obj = "Hello";
if (obj is string)
{
Console.WriteLine("obj is a string");
// Pattern matching with is (C# 7.0+)
if (obj is string str)
{
Console.WriteLine($"String length: {str.Length}");
}
}
// as operator
object obj2 = "World";
string str2 = obj2 as string; // If obj2 is not a string, str2 will be null
if (str2 != null)
{
Console.WriteLine($"String: {str2}");
}
// Instead of using cast which throws an exception if fails:
// string str3 = (string)obj2; // Would throw InvalidCastException if obj2 is not a string
// typeof operator
Type stringType = typeof(string);
Console.WriteLine($"Type name: {stringType.Name}"); // Output: "String"
Console.WriteLine($"Is value type: {stringType.IsValueType}"); // Output: "False"
Type testing operators are essential for robust type checking and conversion in C#. They help avoid runtime exceptions by providing safe ways to check and convert types.
Operator Precedence and Associativity
Operators in C# follow a precedence order that determines the order of operations in expressions. When operators have the same precedence, their associativity determines the order of evaluation.
Operator Precedence (from highest to lowest):
- Primary operators:
x.y,f(x),a[i],x++,x--,new,typeof,checked,unchecked - Unary operators:
+,-,!,~,++x,--x,(T)x - Multiplicative operators:
*,/,% - Additive operators:
+,- - Shift operators:
<<,>> - Relational operators:
<,>,<=,>=,is,as - Equality operators:
==,!= - Bitwise AND:
& - Bitwise XOR:
^ - Bitwise OR:
| - Logical AND:
&& - Logical OR:
|| - Conditional operator:
?: - Assignment operators:
=,+=,-=, etc.
Examples of Precedence:
// Arithmetic precedence
int result = 5 + 3 * 2; // 3 * 2 is evaluated first, then added to 5
Console.WriteLine(result); // Output: 11
// Using parentheses to change precedence
int result2 = (5 + 3) * 2; // 5 + 3 is evaluated first, then multiplied by 2
Console.WriteLine(result2); // Output: 16
// Complex expression
bool result3 = 10 > 5 && 3 < 7 || 4 == 4; // Evaluated as: (10 > 5 && 3 < 7) || 4 == 4
Console.WriteLine(result3); // Output: True
When in doubt about operator precedence, it's a good practice to use parentheses to make your intentions explicit. This improves code readability and prevents unintended behaviors.
Best Practices for Using Operators
- Use parentheses to make complex expressions more readable and to explicitly define precedence
- Be cautious with integer division (/) which truncates the result
- Avoid overly complex expressions; break them down into simpler parts
- Be careful with side effects in operators like ++ and --
- Use the null-conditional and null-coalescing operators to write more concise code when dealing with null values
- Consider potential overflow when working with arithmetic operations on values close to the limits of their data types
- Use the is operator with pattern matching for more readable type checks