The symbols that tell C# what to do with your data.
Imagine you want to build a simple calculator application. You need a way to add two numbers, compare which one is larger, and decide what to do based on a condition. Without symbols like +, >, and &&, you'd have to write thousands of lines of low-level code just to add 2 + 3.
Operators are the shorthand that makes programming practical. They are the verbs in your code sentences — they act on values and produce results. Understanding operators is foundational because every single program uses them constantly.
In this lesson, you'll learn the different categories of operators, how they work, precedence rules, and common pitfalls.
An operator is a symbol that tells the compiler to perform a specific operation on one or more values (called operands).
In C#, operators are special symbols that perform operations on operands. C# supports a rich set of operators, which can be classified into:
+, -, *, /, %)bool (==, !=, <, >, <=, >=)&&, ||, !)=, +=, -=, *=, etc.)&, |, ^, ~, <<, >>)??, ??=)condition ? trueValue : falseValueis, as, typeof, sizeof. (dot) and [] (indexer)Programs need to manipulate data. Without operators, you would have to call methods for every operation:
int result = Add(2, 3); // instead of 2 + 3
bool isEqual = AreEqual(a, b); // instead of a == b
This would make code verbose, hard to read, and error-prone. Operators provide a concise, universally understood syntax.
C# defines a set of built-in operators that map directly to CPU instructions or common patterns. They allow you to write expressions naturally:
int result = 2 + 3;
bool isEqual = a == b;
Additionally, C# allows you to overload many operators for your own custom types, enabling intuitive syntax like vector1 + vector2.
Operators are not magic — they are just concise ways to invoke predefined or user-defined methods. The compiler translates operators into method calls or low-level instructions.
Operators are the glue in expressions. They determine how data flows and transforms:
total = price * quantity + tax
Let's trace how the compiler and runtime evaluate an expression with multiple operators:
The compiler scans the source code and identifies operators and operands. For a + b * c, it sees + and *.
Each operator has a precedence. * has higher precedence than +, so b * c is evaluated first. If operators have the same precedence, associativity (left-to-right or right-to-left) determines order.
Operands are evaluated, possibly involving function calls, variable reads, or side effects. For ++i, the increment happens before the value is used; for i++, after.
The operator's underlying implementation is invoked: either a built-in CPU instruction (e.g., add) or an overloaded method for custom types.
For && and ||, the right operand is not evaluated if the left operand determines the result. This is crucial for code like if (obj != null && obj.IsValid).
The resulting value is used in the outer expression or assigned to a variable.
using System;
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)
int remainder = a % b; // 1
bool isGreater = a > b; // true
bool isEqual = a == b; // false
bool bothPositive = (a > 0) && (b > 0); // true
bool eitherNegative = (a < 0) || (b < 0); // false
string? name = null;
string displayName = name ?? "Guest"; // "Guest"
// Conditional operator
int max = (a > b) ? a : b; // 10
Console.WriteLine($"sum={sum}, quotient={quotient}, max={max}, displayName={displayName}");
Code → Meaning → Result
+ adds, - subtracts, * multiplies, / divides (note integer division truncates), % gives remainder.> and == compare and return bool.&& and || combine booleans with short-circuiting.?? returns the left operand if not null, otherwise the right.?: returns one of two values based on a condition.Consider an e-commerce order processing function that calculates discount based on membership level and order total:
decimal orderTotal = 250.00m;
bool isMember = true;
bool hasCoupon = false;
decimal discount = 0m;
// Apply member discount: 10%
if (isMember)
{
discount += orderTotal * 0.10m;
}
// Apply coupon discount: fixed $20 if coupon available and total > 100
if (hasCoupon && orderTotal > 100)
{
discount += 20m;
}
// Ensure discount doesn't exceed total
discount = Math.Min(discount, orderTotal);
decimal finalTotal = orderTotal - discount;
Console.WriteLine($"Order total: {orderTotal:C}, Discount: {discount:C}, Final: {finalTotal:C}");
This uses arithmetic operators (*, +, -), comparison (>), logical (&&), assignment (+=), and method call Math.Min. It's typical in any business application.
Arithmetic operators are like wrenches and hammers — they shape raw materials.
Comparison operators are like measuring tapes — they tell you how one thing relates to another.
Logical operators are like decision gates — they combine conditions to decide a path.
Assignment operators are like labels — they place a value into a named container.
Just as a carpenter selects the right tool for the job, a developer selects the right operator for the computation or decision.
This analogy helps you remember that operators are not just symbols; they are purposeful tools with specific behaviours.
What actually happens when you use operators?
add, sub, mul, div, rem.ceq, cgt, clt, etc.&& and || generate branch instructions for short-circuiting.checked context, overflow throws OverflowException.checked block or expression to opt in.is pattern matching and ??= are fully supported.checked operators (C# 11) allow you to define separate operators for checked contexts.INumber<T>) provide operator support for generic types.= vs === is assignment; == is equality comparison.
int x = 5; // assign 5 to x
if (x == 5) { /* true */ } // compare x to 5
Using = in an if condition is a compile error in C# (unlike C/C++ where it's allowed and often a bug).
&& vs &, || vs |&& and || are short-circuiting logical operators. They evaluate the second operand only if necessary. & and | are non-short-circuiting; they always evaluate both operands and also perform bitwise operations when used with integers.
/ with integers vs floating-pointInteger division truncates toward zero: 7 / 2 yields 3, not 3.5. To get floating-point division, at least one operand must be a floating-point type: 7.0 / 2 or (double)7 / 2.
Multiplication and division have higher precedence than addition and subtraction. Use parentheses to make your intent explicit when in doubt.
int result = 2 + 3 * 4; // 14, not 20
int better = (2 + 3) * 4; // 20
The ternary operator ?: is an expression, not a statement. It returns a value and cannot contain statements. Use it for simple conditional value assignments; use if-else for complex logic.
double average = sum / count; — if both are int, division is integer, then converted to double (already truncated).
double average = (double)sum / count; or double average = sum * 1.0 / count;
if (CheckSomething() && CheckAnother()) — if CheckSomething() returns false, CheckAnother() is not called. This is usually desired but can cause missed side effects if you intended both to run.
If you need both to execute, call them separately and store results.
== for reference equality on stringsActually == is overloaded for strings to compare values, so it's fine. But for other reference types, == compares references. Use .Equals() or override == if you need value comparison.
== without != (or vice versa)If you overload ==, you must also overload != (and it's good practice to override Equals and GetHashCode). Similarly for < and >, <= and >=.
?? with || for null checks?? returns the right operand only if the left is null; it doesn't treat other falsy values like false or 0 as null.
You use operators in virtually every line of C# code. Specific guidance:
++, --).if statements, loops, sorting.[Flags]).&& and || short-circuit&&, ||) can skip evaluation of the right operand.double for floating-point division.= assigns, == compares.??=), pattern matching with is, and generic math operators via INumber<T>.You've seen how operators work, why precedence matters, and how to avoid common pitfalls. Let's test your understanding.
1. What is the value of result after this code runs?
int result = 7 / 2;
Correct: A
Why A is correct: Both operands are integers, so C# performs integer division, which truncates the fractional part. 7 / 2 yields 3 (not 3.5). The result is an int, so 3 is correct.
Why B is incorrect: That would be the result of floating-point division. Integer division does not produce a fractional part.
Why C is incorrect: Integer division truncates toward zero, it does not round to nearest.
Why D is incorrect: The result is an int, not a double, so 3.0 is not possible.
Reinforcement: To get a floating-point result, cast one operand to double or float: 7.0 / 2 or (double)7 / 2.
2. Which of the following correctly uses short-circuit evaluation to safely check if a string is not null and has length greater than 0?
Correct: A
Why A is correct: The && operator short-circuits: if str != null is false, the right side (str.Length > 0) is not evaluated, preventing a NullReferenceException.
Why B is incorrect: The & operator does not short-circuit; both sides are always evaluated. If str is null, str.Length throws a NullReferenceException.
Why C is incorrect: The order is wrong; it would attempt to access str.Length before checking null, causing an exception if str is null.
Why D is incorrect: ?? is null-coalescing; it doesn't combine boolean conditions.
Reinforcement: Use && for null checks and condition ordering. Always put null checks first.
3. Which expression is equivalent to x = x + 5?
Correct: A
Why A is correct: The compound assignment operator += is shorthand for adding the right operand to the left variable and assigning the result back. x += 5 is exactly equivalent to x = x + 5.
Why B is incorrect: =+ is not a valid compound assignment operator in C#.
Why C is incorrect: While x = 5 + x produces the same result, it's not the compound assignment operator; the question specifically asks for equivalent syntax.
Why D is incorrect: x++ 5 is invalid syntax.
Reinforcement: Compound assignment operators (+=, -=, *=, etc.) are concise and often more readable.
4. What is the output of the following code?
bool a = true;
bool b = false;
bool result = a || b && false;
Console.WriteLine(result);
Correct: A
Why A is correct: Due to operator precedence, && has higher precedence than ||. So b && false is evaluated first: false && false = false. Then a || false = true || false = true. Therefore result is true.
Why B is incorrect: This would only be true if || had higher precedence, but it doesn't.
Why C is incorrect: The expression is perfectly valid C#.
Why D is incorrect: There is no runtime error.
Reinforcement: Remember precedence: ! > && > ||. Use parentheses to make your intent explicit.
5. When should you use the null-coalescing assignment operator ??=?
Correct: A
Why A is correct: The null-coalescing assignment operator ??= assigns the right operand to the left operand only if the left operand is null. It's a concise way to initialize a variable if it hasn't been set.
Why B is incorrect: That would be the opposite; there's no built-in operator for "assign only if not null" (you'd use a regular if statement).
Why C is incorrect: String concatenation uses + or interpolation, not ??=.
Why D is incorrect: ??= is an assignment operator, not a comparison.
Reinforcement: ??= is useful for lazy initialization: cache ??= ComputeValue();
You've now mastered the fundamentals of C# operators — the building blocks of all logic!
dotnetmadeeasy.com — Learn C# and .NET, the right way.