C# Operators Complete Guide (Bitwise, Nullable, is/as & Precedence) Masterclass
Welcome to Phase 3 (Chapter 7): C# Operators, Bitwise, Nullable & Precedence Masterclass! Operators are fundamental tokens that instruct the compiler to perform specific mathematical, logical, relational, bitwise, or type operations. In this comprehensive textbook guide, we explore all C# operators: arithmetic, assignment, compound assignment, comparison, logical, increment/decrement, unary, ternary (?:), null-coalescing (??), null-conditional (?.), bitwise operators, type-testing operators (is & as), and operator precedence rules.
Arithmetic operators perform basic mathematical calculations. Compound assignment operators combine arithmetic with assignment to write shorter, cleaner expressions.
| Operator | Category | Description | Example |
|---|---|---|---|
+ | Arithmetic | Addition (or String concatenation) | 5 + 3 == 8 |
- | Arithmetic | Subtraction | 10 - 4 == 6 |
* | Arithmetic | Multiplication | 4 * 5 == 20 |
/ | Arithmetic | Division (integer division truncates decimal!) | 7 / 2 == 3 (int) | 7.0 / 2 == 3.5 |
% | Arithmetic | Modulus (remainder of division) | 17 % 5 == 2 |
+= | Compound | Add and Assign | x += 5 (same as x = x + 5) |
-= | Compound | Subtract and Assign | x -= 3 |
*= | Compound | Multiply and Assign | x *= 2 |
/= | Compound | Divide and Assign | x /= 4 |
%= | Compound | Modulus and Assign | x %= 3 |
int price = 100;
int discount = 20;
int finalPrice = price - discount;
Console.WriteLine($"Price: {price}, Discount: {discount}, Final Price: {finalPrice}");
// Compound assignment
int score = 50;
score += 10; // 60
score *= 2; // 120
score %= 7; // 1
Console.WriteLine($"Calculated Score: {score}");
// Pre-increment vs Post-increment
int count = 5;
Console.WriteLine($"Pre-increment (++count): {++count}"); // Outputs 6 (increments first)
Console.WriteLine($"Post-increment (count++): {count++}"); // Outputs 6 (prints, then increments to 7)
Console.WriteLine($"Final count value: {count}"); // Outputs 7
price - discount: Subtracts integer 20 from 100, storing 80 infinalPrice.++count(Pre-increment): Modifies the variable before evaluating the surrounding expression.count++(Post-increment): Evaluates the surrounding expression using the original value before incrementing memory.
Comparison operators compare two values and return a boolean (true or false). Logical operators combine multiple boolean expressions.
int age = 20;
bool hasID = true;
// Comparison: ==, !=, >, <, >=, <=
// Logical AND (&&): true ONLY IF both conditions are true
bool canEnterClub = (age >= 18) && hasID;
Console.WriteLine($"Can enter club: {canEnterClub}");
// Logical OR (||): true IF AT LEAST ONE condition is true
bool isWeekend = true;
bool isHoliday = false;
bool canRest = isWeekend || isHoliday;
Console.WriteLine($"Can rest: {canRest}");
// Logical NOT (!): Inverts boolean value
bool isRaining = false;
Console.WriteLine($"Is clear weather: {!isRaining}");
// Ternary Operator (condition ? valueIfTrue : valueIfFalse)
string status = (age >= 18) ? "Adult" : "Minor";
Console.WriteLine($"User Status: {status}");
C# provides built-in operators specifically designed to handle null values safely without throwing dangerous NullReferenceException crashes:
string? name = null;
// 1. Null-Conditional Operator (?.) โ Safe property access
// If name is null, returns null instead of throwing NullReferenceException!
int? nameLength = name?.Length;
Console.WriteLine($"Length: {nameLength?.ToString() ?? "null"}");
// 2. Null-Coalescing Operator (??) โ Fallback value if null
string displayName = name ?? "Guest User";
Console.WriteLine($"Hello, {displayName}!");
// 3. Null-Coalescing Assignment Operator (??=) โ Assigns value ONLY IF variable is null
name ??= "Default Ravi";
Console.WriteLine($"Name after ??=: {name}");
When working with object hierarchies or interface references, the is and as operators provide safe type checking and casting:
object data = "Hello C# Masterclass";
// 1. 'is' operator with pattern matching variable declaration
if (data is string text)
{
Console.WriteLine($"data is a string of length {text.Length}: '{text}'");
}
// 2. 'as' operator (safe cast โ returns null if conversion fails, NO EXCEPTION!)
string? strVal = data as string;
if (strVal != null)
{
Console.WriteLine($"Safe cast string upper: {strVal.ToUpper()}");
}
object numObj = 42;
string? badCast = numObj as string; // badCast is null (does not throw exception!)
Console.WriteLine($"Bad cast result: {badCast ?? "NULL"}");
Bitwise operators manipulate data at the individual binary bit level (0 and 1). They are heavily used in graphics programming, low-level networking, cryptography, and flags.
| Operator | Name | Operation | Example (a=5 / 0101, b=3 / 0011) |
|---|---|---|---|
& | Bitwise AND | 1 if both bits are 1 | 5 & 3 == 1 (0001) |
| | Bitwise OR | 1 if at least one bit is 1 | 5 | 3 == 7 (0111) |
^ | Bitwise XOR | 1 if bits are different | 5 ^ 3 == 6 (0110) |
~ | Bitwise NOT | Inverts all bits | ~5 == -6 |
<< | Left Shift | Shifts bits left (multiplies by 2^n) | 5 << 1 == 10 |
>> | Right Shift | Shifts bits right (divides by 2^n) | 5 >> 1 == 2 |
When an expression contains multiple operators, operator precedence determines the order of evaluation (from highest to lowest):
| Precedence Rank | Category | Operators |
|---|---|---|
| 1 (Highest) | Primary / Postfix | x.y, x?.y, f(x), a[i], x++, x--, new |
| 2 | Unary | +x, -x, !x, ~x, ++x, --x, (Type)x, await |
| 3 | Multiplicative | *, /, % |
| 4 | Additive | +, - |
| 5 | Shift | <<, >> |
| 6 | Relational & Type | <, >, <=, >=, is, as |
| 7 | Equality | ==, != |
| 8 | Logical AND | & |
| 9 | Logical XOR | ^ |
| 10 | Logical OR | | |
| 11 | Conditional AND | && |
| 12 | Conditional OR | || |
| 13 | Null-Coalescing | ?? |
| 14 | Ternary | c ? t : f |
| 15 (Lowest) | Assignment | =, +=, -=, *=, /=, %=, ??= |
Q1: What is short-circuit evaluation in && and || operators?
Short-circuit evaluation means the compiler stops evaluating an expression as soon as the result is determined. For &&, if the first condition is false, the second condition is completely skipped. For ||, if the first condition is true, the second condition is skipped.
Q2: How does the 'as' operator differ from explicit casting (Type)obj?
Explicit casting (string)obj throws an InvalidCastException if the object is not of that type. The as operator performs a safe cast and returns null if the cast fails without throwing any exception.
Q3: What is the difference between ?? and ??= ?
a ?? b returns b if a is null. a ??= b assigns b to a ONLY IF a is currently null.