Operators
Operators combine values into new values: arithmetic, comparisons, logical tests, assignments and a few C#-specific shortcuts for working with null. This lesson gathers them together and explains the rules for which operator runs first.
Assignment and compound assignment
= stores a value. The compound forms update a variable using its current value:
int points = 10; points += 5; // 15 points -= 3; // 12 points *= 2; // 24 points /= 5; // 4 (integer division) points %= 3; // 1 Console.WriteLine(points); string log = "start"; log += " -> step"; Console.WriteLine(log);
1 start -> step
Increment and decrement
++ adds 1 and -- subtracts 1. Placed before the variable (prefix), the new value is used; placed after (postfix), the old value is used and then the variable changes:
int i = 5; Console.WriteLine(i++); // prints 5, then i becomes 6 Console.WriteLine(i); Console.WriteLine(++i); // i becomes 7, then prints 7 Console.WriteLine(i--); // prints 7, then i becomes 6 Console.WriteLine(i);
5 6 7 7 6
On a line by itself (count++;) the two forms do the same thing. Avoid using ++ inside larger expressions — it makes code hard to read.
Comparison operators
Comparisons produce a bool:
int a = 7, b = 10; Console.WriteLine(a == b); // equal Console.WriteLine(a != b); // not equal Console.WriteLine(a < b); Console.WriteLine(a >= 7); Console.WriteLine("apple" == "apple");
False True True True True
For strings, == compares the text, not whether they're the same object. Note the difference between = (assign) and == (compare); C# won't let you accidentally write if (a = 5), because the result isn't a bool.
Logical operators
| Operator | Meaning | True when |
|---|---|---|
&& | and | both sides are true |
|| | or | at least one side is true |
! | not | the operand is false |
^ | exclusive or | exactly one side is true |
int age = 25; bool hasLicence = true; bool isWeekend = false; Console.WriteLine(age >= 18 && hasLicence); Console.WriteLine(isWeekend || age > 65); Console.WriteLine(!isWeekend); Console.WriteLine(hasLicence ^ isWeekend);
True False True True
Short-circuit evaluation
&& and || stop as soon as the answer is known. If the left side of && is false, the right side never runs. This lets you guard a risky check with a safe one:
int[] scores = []; if (scores.Length > 0 && scores[0] > 50) { Console.WriteLine("First score passed"); } else { Console.WriteLine("No passing first score"); }
No passing first score
Without short-circuiting, scores[0] would run on an empty array and crash.
The conditional operator
condition ? a : b picks one of two values. It's an expression, so it can go anywhere a value can:
int stock = 0; string label = stock > 0 ? "In stock" : "Sold out"; Console.WriteLine(label); int n = 3; Console.WriteLine($"{n} item{(n == 1 ? "" : "s")}");
Sold out 3 items
Null operators
C# has operators for dealing with values that might be null. ?? supplies a fallback, ??= assigns only if the variable is null, and ?. calls a member only if the object isn't null. Lesson 43 covers them fully.
string? nickname = null; Console.WriteLine(nickname ?? "(no nickname)"); Console.WriteLine(nickname?.Length); // null: prints an empty line nickname ??= "Sunny"; Console.WriteLine(nickname); Console.WriteLine(nickname?.Length);
(no nickname) Sunny 5
Precedence
When an expression mixes operators, higher-precedence operators run first: multiplication before addition, comparison before &&, && before ||. Parentheses override the order — and make your intent clear:
Console.WriteLine(2 + 3 * 4); Console.WriteLine((2 + 3) * 4); Console.WriteLine(10 - 4 - 3); // left to right: (10 - 4) - 3 Console.WriteLine(true || false && false); Console.WriteLine((true || false) && false);
14 20 3 True False
| Precedence (high to low) | Operators |
|---|---|
| Primary | x.y x?.y f(x) a[i] x++ x-- |
| Unary | ! -x ++x --x (T)x |
| Multiplicative | * / % |
| Additive | + - |
| Comparison | < > <= >= is as |
| Equality | == != |
| Logical | && then || |
| Null-coalescing | ?? |
| Conditional | ?: |
| Assignment | = += ??= … |
Bitwise operators
&, |, ^, ~, << and >> work on the individual bits of integers. You'll mostly meet them with flag enums (lesson 41):
int a = 0b1100, b = 0b1010; Console.WriteLine(Convert.ToString(a & b, 2)); // 1000 Console.WriteLine(Convert.ToString(a | b, 2)); // 1110 Console.WriteLine(Convert.ToString(a ^ b, 2)); // 0110 Console.WriteLine(1 << 4); // 16 Console.WriteLine(256 >> 2); // 64
1000 1110 110 16 64
Try it yourself
A year is a leap year if it's divisible by 4, except century years, which must be divisible by 400. Test a few years.
1900: common 2000: leap 2024: leap 2026: common