Section 4 of 10

Operators and Expressions

🎯 What You'll Learn

  • What operators and expressions are
  • Arithmetic operators for calculations (+, -, *, /, %)
  • Comparison operators for making decisions (==, !=, <, >, etc.)
  • Logical operators for combining conditions (&&, ||, !)
  • Assignment operators and compound assignment (+=, -=, etc.)
  • The powerful null-handling operators (?., ??, ??=)
  • The ternary conditional operator (?:)
  • Operator precedence and why parentheses matter
  • Common string operations

What Are Operators and Expressions?

In the previous section, you learned how to store data in variables. But storing data is only useful if you can do something with it—calculate totals, compare values, make decisions. That's where operators come in.

💡 Key Concepts

An operator is a symbol that tells the compiler to perform a specific operation (like addition or comparison).

An expression is a combination of values, variables, and operators that evaluates to a single result.

ExpressionsIntro.cs C#
// These are all expressions:
5 + 3                      // Evaluates to 8
price * quantity           // Evaluates to a decimal
age >= 18                  // Evaluates to true or false
firstName + " " + lastName // Evaluates to a string
isActive && hasPermission  // Evaluates to true or false

Every expression produces a value. That value can be stored in a variable, passed to a method, used in a condition, or combined with other expressions.

Arithmetic Operators

Arithmetic operators perform mathematical calculations. These are the operators you'd expect from basic math class.

Basic Arithmetic

Operator Name Example Result
+ Addition 10 + 3 13
- Subtraction 10 - 3 7
* Multiplication 10 * 3 30
/ Division 10 / 3 3 (integer division!)
% Modulus (remainder) 10 % 3 1
ArithmeticOperators.cs C#
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 (not 3.33!)
int remainder = a % b;   // 1

// Unary operators (operate on a single value)
int positive = +a;        // 10 (rarely used)
int negative = -a;        // -10 (negation)
⚠️ Integer Division Trap

When you divide two integers, C# performs integer division—the result is truncated (not rounded). 10 / 3 equals 3, not 3.33. If you need decimal results, at least one operand must be a floating-point type.

DivisionTypes.cs C#
// Integer division - decimal part is LOST
int result1 = 10 / 3;           // 3 (truncated)
int result2 = 7 / 2;            // 3 (not 3.5)

// Floating-point division - preserves decimals
double result3 = 10.0 / 3;       // 3.3333...
double result4 = 10 / 3.0;       // 3.3333...
double result5 = (double)10 / 3; // 3.3333... (cast to force floating-point)

// For money calculations, use decimal
decimal total = 100.00m;
decimal perPerson = total / 3;  // 33.3333...

The Modulus Operator (%)

The modulus operator returns the remainder after division. It's incredibly useful for many programming tasks:

ModulusOperator.cs C#
// Basic modulus - what's left over?
int remainder = 17 % 5;  // 2 (17 = 5*3 + 2)

// Check if a number is even or odd
int number = 42;
bool isEven = number % 2 == 0;  // true (42 is even)
bool isOdd = number % 2 != 0;   // false

// Check if divisible by something
int year = 2024;
bool divisibleBy4 = year % 4 == 0;    // true (useful for leap years)
bool divisibleBy100 = year % 100 == 0; // false

// Wrap around (cycling through values)
int hour = 25;
int hour24 = hour % 24;  // 1 (wraps around after 24)

// Get the last digit of a number
int lastDigit = 12345 % 10;  // 5

// Alternating rows in a table (for styling)
for (int row = 0; row < 10; row++)
{
    bool isAlternateRow = row % 2 == 0;  // true for 0, 2, 4, 6, 8
}

Increment and Decrement

The ++ and -- operators add or subtract 1 from a variable. They can be placed before (prefix) or after (postfix) the variable, with a subtle difference.

IncrementDecrement.cs C#
int count = 5;

count++;      // count is now 6 (same as count = count + 1)
count--;      // count is now 5 (same as count = count - 1)

// Prefix vs Postfix - the difference matters when used in expressions
int a = 5;
int b = a++;   // b = 5, THEN a becomes 6 (postfix: use, then increment)

int c = 5;
int d = ++c;   // c becomes 6 FIRST, then d = 6 (prefix: increment, then use)

// Most common usage: standalone statements (prefix vs postfix doesn't matter)
int counter = 0;
counter++;     // Simple increment

// Common in loops (we'll cover loops in Control Flow)
for (int i = 0; i < 10; i++)
{
    // i increments each iteration
}
💡 Keep It Simple

The prefix/postfix difference can be confusing. As a best practice, avoid using ++ or -- inside larger expressions. Use them as standalone statements (count++;) where the difference doesn't matter.

Comparison Operators

Comparison operators compare two values and return a bool (true or false). They're essential for making decisions in your code.

Operator Name Example Result
== Equal to 5 == 5 true
!= Not equal to 5 != 3 true
> Greater than 5 > 3 true
< Less than 5 < 3 false
>= Greater than or equal 5 >= 5 true
<= Less than or equal 5 <= 3 false
ComparisonOperators.cs C#
int age = 25;
int minimumAge = 18;
int retirementAge = 65;

bool isAdult = age >= minimumAge;       // true
bool canRetire = age >= retirementAge;   // false
bool isExactly25 = age == 25;           // true
bool isNot25 = age != 25;               // false

// Works with other types too
decimal price = 99.99m;
decimal budget = 100.00m;
bool canAfford = price <= budget;       // true

// String comparison (case-sensitive by default)
string input = "yes";
bool isYes = input == "yes";            // true
bool isYES = input == "YES";            // false (case matters!)

// Case-insensitive string comparison
bool isYesIgnoreCase = input.Equals("YES", StringComparison.OrdinalIgnoreCase); // true
⚠️ Common Mistake: = vs ==

= is assignment (put a value into a variable).
== is comparison (check if two values are equal).

Writing if (x = 5) instead of if (x == 5) is a common bug. Fortunately, C# usually catches this with a compiler error.

Logical Operators

Logical operators combine multiple boolean expressions. They're how you express conditions like "is active AND has permission" or "is admin OR is owner."

Operator Name Description Example
&& Logical AND True if BOTH are true true && falsefalse
|| Logical OR True if EITHER is true true || falsetrue
! Logical NOT Inverts the value !truefalse

Truth Tables

These tables show the result of each logical operator for all possible inputs:

AND (&&)
ABA && B
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse
OR (||)
ABA || B
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse
NOT (!)
A!A
truefalse
falsetrue
LogicalOperators.cs C#
// AND - both conditions must be true
bool isActive = true;
bool hasPermission = true;
bool canAccess = isActive && hasPermission;  // true (both are true)

// OR - at least one condition must be true
bool isAdmin = false;
bool isOwner = true;
bool canEdit = isAdmin || isOwner;          // true (owner is true)

// NOT - inverts the value
bool isLoggedIn = true;
bool isGuest = !isLoggedIn;                  // false

// Real-world examples
int age = 25;
bool hasLicense = true;
bool canDrive = age >= 18 && hasLicense;    // Must be adult AND have license

decimal orderTotal = 150m;
bool isPremiumMember = false;
bool hasFreeShipping = orderTotal >= 100m || isPremiumMember;  // true

// Combining multiple conditions
int quantity = 50;
bool inStock = true;
bool canFulfillOrder = inStock && quantity > 0 && quantity <= 100;

// Using parentheses for clarity (and correctness)
bool complexCondition = (isAdmin || isOwner) && isActive;
// Different from: isAdmin || (isOwner && isActive)

Short-Circuit Evaluation

C#'s && and || operators use short-circuit evaluation—they stop as soon as the result is determined:

ShortCircuit.cs C#
// AND short-circuits: if the first is false, the second is never evaluated
bool result1 = false && SomeExpensiveFunction();  // Function never called!

// OR short-circuits: if the first is true, the second is never evaluated
bool result2 = true || SomeExpensiveFunction();   // Function never called!

// This is useful for null checks!
string name = null;

// Without short-circuit: would crash with NullReferenceException
// bool valid = name.Length > 0;  // CRASH!

// With short-circuit: safe because Length is never accessed if name is null
bool valid = name != null && name.Length > 0;  // false (no crash)
💡 Use Short-Circuiting for Safety

Always put null checks before accessing properties: obj != null && obj.Property. The property access only happens if the null check passes.

Assignment Operators

Assignment operators store values in variables. Beyond the basic =, there are compound operators that combine assignment with arithmetic.

Operator Example Equivalent To
= x = 5 Assign 5 to x
+= x += 3 x = x + 3
-= x -= 3 x = x - 3
*= x *= 3 x = x * 3
/= x /= 3 x = x / 3
%= x %= 3 x = x % 3
AssignmentOperators.cs C#
int score = 100;

score += 10;   // score is now 110
score -= 5;    // score is now 105
score *= 2;    // score is now 210
score /= 3;    // score is now 70

// String concatenation with +=
string message = "Hello";
message += " World";  // "Hello World"

// Common pattern: accumulating a total
decimal total = 0m;
total += 19.99m;  // Add first item
total += 29.99m;  // Add second item
total += 9.99m;   // Add third item
// total is now 59.97

Null-Handling Operators

Null references are a common source of bugs. C# provides special operators to handle null values elegantly. These are extremely useful in real-world code.

Null-Conditional Operator (?. and ?[])

The ?. operator only accesses a member if the object is not null. If the object is null, the entire expression returns null instead of crashing.

NullConditional.cs C#
string name = null;

// Without null-conditional: crashes with NullReferenceException!
// int length = name.Length;  // CRASH!

// With null-conditional: returns null instead of crashing
int? length = name?.Length;  // null (no crash)

// Chaining multiple levels
string city = customer?.Address?.City;  // Safe even if customer or Address is null

// With array indexer
int[]? numbers = null;
int? firstNumber = numbers?[0];  // null (no crash)

// With method calls
string upper = name?.ToUpper();  // null (no crash)

Null-Coalescing Operator (??)

The ?? operator provides a default value when something is null. Think of it as "use this value, or if it's null, use that value instead."

NullCoalescing.cs C#
string? input = null;
string result = input ?? "default";  // "default" (because input is null)

string? input2 = "hello";
string result2 = input2 ?? "default";  // "hello" (input2 is not null)

// Common use: providing default values
string displayName = user.Nickname ?? user.FullName ?? "Anonymous";

// With nullable value types
int? quantity = null;
int actualQuantity = quantity ?? 0;  // 0

// Combining with null-conditional
int nameLength = name?.Length ?? 0;  // 0 if name is null

Null-Coalescing Assignment (??=)

The ??= operator assigns a value only if the variable is currently null.

NullCoalescingAssignment.cs C#
string? name = null;
name ??= "Unknown";  // name is now "Unknown"

string? name2 = "Akwasi";
name2 ??= "Unknown";  // name2 is still "Akwasi" (not null, so no assignment)

// Equivalent to:
if (name == null)
{
    name = "Unknown";
}

// Useful for lazy initialization
private List<string>? _items;
public List<string> Items => _items ??= new List<string>();
💡 Null Operators Summary

?. — Safe member access (returns null if object is null)
?? — Provide default value if null
??= — Assign only if currently null

Master these three operators—you'll use them constantly in real C# code.

The Ternary Conditional Operator (?:)

The ternary operator is a compact way to choose between two values based on a condition. It's like an inline if-else.

TernaryOperator.cs C#
// Syntax: condition ? valueIfTrue : valueIfFalse

int age = 20;
string status = age >= 18 ? "Adult" : "Minor";  // "Adult"

// Equivalent if-else:
string status2;
if (age >= 18)
    status2 = "Adult";
else
    status2 = "Minor";

// Common use cases
int quantity = 5;
string itemText = quantity == 1 ? "item" : "items";  // "items"
Console.WriteLine($"You have {quantity} {itemText}");  // "You have 5 items"

// Finding the larger of two numbers
int a = 10, b = 20;
int max = a > b ? a : b;  // 20

// Nested ternary (use sparingly - can be hard to read)
int score = 85;
string grade = score >= 90 ? "A" 
             : score >= 80 ? "B"
             : score >= 70 ? "C"
             : score >= 60 ? "D"
             : "F";  // "B"

// With null-coalescing for even more power
string? userName = null;
string greeting = $"Hello, {userName ?? "Guest"}!";  // "Hello, Guest!"
💡 Ternary Best Practices

Use ternary for simple, single-line choices. For complex logic or multiple statements, use regular if-else—it's more readable. Avoid deeply nested ternaries.

String Operations

Strings have special behavior with operators and many useful methods. Let's explore the most common operations.

String Concatenation

StringOperations.cs C#
// Concatenation with +
string firstName = "Akwasi";
string lastName = "Asante";
string fullName = firstName + " " + lastName;  // "Akwasi Asante"

// String interpolation (preferred)
string fullName2 = $"{firstName} {lastName}";  // "Akwasi Asante"

// Interpolation with formatting
decimal price = 1234.567m;
string formatted = $"Price: {price:C}";        // "Price: $1,234.57" (currency)
string twoDecimals = $"Price: {price:F2}";     // "Price: 1234.57" (2 decimals)
string padded = $"ID: {42:D5}";                // "ID: 00042" (padded to 5 digits)

DateTime now = DateTime.Now;
string dateStr = $"Today: {now:yyyy-MM-dd}";   // "Today: 2024-01-15"

String Comparison

StringComparison.cs C#
string a = "hello";
string b = "HELLO";

// Case-sensitive comparison (default)
bool equal1 = a == b;                                        // false
bool equal2 = a.Equals(b);                                    // false

// Case-insensitive comparison
bool equal3 = a.Equals(b, StringComparison.OrdinalIgnoreCase);  // true
bool equal4 = string.Equals(a, b, StringComparison.OrdinalIgnoreCase);  // true

// Comparing with ToLower/ToUpper (works but less efficient)
bool equal5 = a.ToLower() == b.ToLower();                    // true

Common String Methods

StringMethods.cs C#
string text = "  Hello, World!  ";

// Length
int length = text.Length;                  // 17

// Trimming whitespace
string trimmed = text.Trim();               // "Hello, World!"
string trimStart = text.TrimStart();        // "Hello, World!  "
string trimEnd = text.TrimEnd();            // "  Hello, World!"

// Case conversion
string upper = text.ToUpper();              // "  HELLO, WORLD!  "
string lower = text.ToLower();              // "  hello, world!  "

// Searching
bool contains = text.Contains("World");     // true
bool startsWith = text.StartsWith("  H");  // true
bool endsWith = text.EndsWith("!");        // false (trailing spaces!)
int index = text.IndexOf("World");         // 9

// Substring
string sub1 = text.Substring(2);            // "Hello, World!  " (from index 2)
string sub2 = text.Substring(2, 5);         // "Hello" (5 chars from index 2)

// Replace
string replaced = text.Replace("World", "C#");  // "  Hello, C#!  "

// Split and Join
string csv = "apple,banana,cherry";
string[] fruits = csv.Split(',');           // ["apple", "banana", "cherry"]
string joined = string.Join(" | ", fruits); // "apple | banana | cherry"

// Check for empty or null
string empty = "";
string? nullStr = null;
bool isEmpty = string.IsNullOrEmpty(empty);      // true
bool isNull = string.IsNullOrEmpty(nullStr);     // true
bool isWhitespace = string.IsNullOrWhiteSpace("   ");  // true
ℹ️ Strings Are Immutable

In C#, strings cannot be changed after creation. Methods like ToUpper() or Replace() return a new string—they don't modify the original. This is why you need to assign the result: text = text.ToUpper();

Operator Precedence

When an expression has multiple operators, C# evaluates them in a specific order called precedence. This is like math's "order of operations" (PEMDAS/BODMAS).

Priority Operators Description
1 (highest) () [] ?. ! Parentheses, member access, null-conditional, not
2 ++ -- (unary) + - Increment, decrement, unary plus/minus
3 * / % Multiplication, division, modulus
4 + - Addition, subtraction
5 < > <= >= Comparison
6 == != Equality
7 && Logical AND
8 || Logical OR
9 ?? Null-coalescing
10 ?: Ternary conditional
11 (lowest) = += -= *= /= Assignment
Precedence.cs C#
// Multiplication before addition
int result1 = 2 + 3 * 4;      // 14, not 20 (3*4 first, then +2)
int result2 = (2 + 3) * 4;    // 20 (parentheses force addition first)

// Comparison before logical operators
bool result3 = 5 > 3 && 2 < 4;  // true (comparisons first, then &&)

// AND before OR (common source of bugs!)
bool a = true, b = false, c = true;
bool result4 = a || b && c;    // true  (b && c first, then a ||)
bool result5 = (a || b) && c;  // true  (parentheses change meaning)
⚠️ When in Doubt, Use Parentheses

Don't rely on memorizing precedence. Use parentheses to make your intent clear. (a && b) || c is easier to read than a && b || c, even though they mean the same thing. Clear code beats clever code.

Putting It All Together: InvenTrack Example

Let's apply what we've learned to a realistic scenario—calculating order totals in our InvenTrack system:

InvenTrackOrderCalculations.cs C#
// Product data
string productName = "Premium Widget";
decimal unitPrice = 79.99m;
int quantity = 5;
decimal? discountPercent = 10m;  // 10% discount, could be null

// Customer data
string? customerName = "Kofi Mensah";
bool isPremiumMember = true;
bool isFirstOrder = false;

// Constants
const decimal VatRate = 0.125m;           // 12.5%
const decimal FreeShippingThreshold = 200m;
const decimal ShippingCost = 15m;

// Calculations
decimal subtotal = unitPrice * quantity;  // 399.95

// Apply discount (use 0 if no discount)
decimal discountRate = (discountPercent ?? 0m) / 100m;  // 0.10
decimal discountAmount = subtotal * discountRate;      // 39.995
decimal afterDiscount = subtotal - discountAmount;     // 359.955

// Calculate VAT
decimal vatAmount = afterDiscount * VatRate;  // 44.994375
decimal totalWithVat = afterDiscount + vatAmount;  // 404.949375

// Determine shipping (free for premium members, high orders, or first order)
bool qualifiesForFreeShipping = isPremiumMember 
                               || totalWithVat >= FreeShippingThreshold 
                               || isFirstOrder;
decimal shipping = qualifiesForFreeShipping ? 0m : ShippingCost;  // 0

// Final total
decimal grandTotal = totalWithVat + shipping;

// Format for display
string displayName = customerName ?? "Guest";
string itemLabel = quantity == 1 ? "item" : "items";
string shippingText = shipping == 0m ? "FREE" : $"{shipping:C}";

// Output
Console.WriteLine($"Order for: {displayName}");
Console.WriteLine($"{quantity} {itemLabel} of {productName}");
Console.WriteLine($"Subtotal:   {subtotal:C}");
Console.WriteLine($"Discount:  -{discountAmount:C}");
Console.WriteLine($"VAT (12.5%): {vatAmount:C}");
Console.WriteLine($"Shipping:   {shippingText}");
Console.WriteLine($"─────────────────────");
Console.WriteLine($"Total:      {grandTotal:C}");

Key Takeaways

  • Expressions combine values, variables, and operators to produce a result
  • Arithmetic operators: + - * / % — watch for integer division truncation!
  • Comparison operators: == != < > <= >= — return bool
  • Logical operators: && || ! — use short-circuit evaluation
  • Compound assignment: += -= *= /= — shorthand for common patterns
  • Null operators: ?. (safe access), ?? (default value), ??= (assign if null)
  • Ternary operator: condition ? ifTrue : ifFalse — inline conditionals
  • Precedence: When in doubt, use parentheses for clarity
🚀 Ready for Control Flow!

You can now perform calculations and comparisons. In the next section, we'll learn how to use these comparisons to control what your program does—making decisions with if statements and repeating actions with loops.

What's Next?

In the next section, Control Flow, we'll learn how to make your programs smart—branching based on conditions (if, switch) and repeating actions (for, while, foreach).