Loops & Conditionals in C#

Loops & Conditionals in C#

Control flow is the "brain" of your application. It allows your program to make decisions, repeat actions, and handle different scenarios based on data.


1. Decision Making: Conditionals

The if, else if, and else Block

The most common way to branch logic.

int score = 85;

if (score >= 90) 
{
    Console.WriteLine("Grade: A");
}
else if (score >= 80) 
{
    Console.WriteLine("Grade: B");
}
else 
{
    Console.WriteLine("Try harder next time!");
}

The Ternary Operator (?:)

A concise way to assign a value based on a condition. Syntax: condition ? value_if_true : value_if_false

int age = 20;
string accessLevel = (age >= 18) ? "Full Access" : "Restricted";

Switch Statements & Supported Types

The switch statement has evolved significantly. Historically, it was very restrictive, but modern C# (7.0 and later) allows you to switch on almost any type using pattern matching.

1. Traditional Constant Switching (Fast & Efficient)

In early versions of C#, you could only switch on types that the compiler could resolve as "constants" at compile time. This allows the compiler to create a Jump Table, which makes the switch statement much faster (O(1) complexity) than a long chain of if-else blocks.

Supported Types for Constants:

  • Integral Types: int, long, byte, short, uint, etc.
  • Char: Single characters ('A', '#').
  • String: Text (C# handles string switching very efficiently using hash codes).
  • Enums: Named constants (the most common use case).
  • Bool: Though rarely used (an if is usually better).

2. Pattern Matching (The "Anything" Switch)

Since C# 7.0, the switch statement is no longer limited to constants. You can now switch on any object type, including custom classes, floats, and decimals, by using Patterns.

object data = 3.14m; // a decimal

switch (data)
{
    case int i:
        Console.WriteLine($"It's an integer: {i}");
        break;
    case decimal d when d > 3:
        Console.WriteLine($"It's a large decimal: {d}");
        break;
    case string s:
        Console.WriteLine($"It's a string of length {s.Length}");
        break;
    case null:
        Console.WriteLine("It's null!");
        break;
}

3. Types Historically "Not Supported" (And Why)

Historically, Floating-Point types (float, double) and Decimal were not allowed in traditional switch statements.

The Reason: Precision & Reliability

  • Equality Issues: Floating-point numbers are notoriously imprecise. 0.1 + 0.2 might result in 0.30000000000000004. If the compiler allowed a case 0.3:, your code might never hit it even if the math "seems" correct.
  • Jump Table Complexity: Unlike integers, which map directly to memory offsets, floating-point ranges are harder to optimize into a fast "jump table."

Note: In modern C#, you CAN use them, but you are usually encouraged to use "relational patterns" (e.g., case > 0.5f:) rather than exact equality checks to avoid these precision bugs.

4. The "No-Go" Zone: What you still can't do

While you can switch on the type of almost anything, you cannot switch on:

  • Expressions that aren't constants in a traditional case (e.g., case someVariable: is illegal; it must be case 10:).
  • Types without a valid equality check or pattern.

Switch Expressions (Modern C# 8.0+)

A more powerful and concise version of the switch statement that returns a value.

string color = "Red";
int priority = color switch 
{
    "Red"    => 1,
    "Yellow" => 2,
    "Green"  => 3,
    _        => 0 // The discard '_' acts as the 'default' case
};

2. Iteration: Loops

The for Loop (Index-Based)

Use this when you know exactly how many times you want to repeat an action.

for (int i = 0; i < 10; i++) 
{
    Console.WriteLine($"Counting: {i}");
}

The foreach Loop (Collection-Based)

The most common loop in C#. It safely iterates over any collection that implements IEnumerable (like List or Array).

string[] fruits = { "Apple", "Banana", "Cherry" };

foreach (var fruit in fruits) 
{
    Console.WriteLine($"Eating a {fruit}");
}

The while Loop (Condition-Based)

Repeats as long as a condition is true. Check the condition before executing.

int energy = 100;
while (energy > 0) 
{
    Console.WriteLine("Working...");
    energy -= 10; // Don't forget to update the condition variable!
}

The do-while Loop

Guarantees at least one execution because the condition is checked after the block.

string input;
do 
{
    Console.WriteLine("Type 'exit' to quit:");
    input = Console.ReadLine();
} while (input != "exit");

3. Control Flow Keywords

  • break: Immediately exits the loop or switch.
  • continue: Skips the rest of the current iteration and jumps to the next one.
  • return: Exits the entire method.

Example: Finding the first even number and stopping

int[] numbers = { 1, 3, 5, 8, 10, 11 };
foreach (var n in numbers) 
{
    if (n % 2 == 0) 
    {
        Console.WriteLine($"First even found: {n}");
        break; // Stops the loop entirely
    }
}

4. Logical & Relational Operators

To build complex conditions, we use:

  • && (AND): True if both sides are true.
  • || (OR): True if at least one side is true.
  • ! (NOT): Reverses the boolean value.
  • == / !=: Equal / Not equal.
  • > / < / >= / <=: Comparison.

Short-Circuiting: C# is smart. In (false && expensiveMethod()), the second part is never run because the whole thing is already guaranteed to be false.


5. Practical Use Cases

Case A: Input Validation

Using do-while to ensure a user enters a valid number.

int validNumber;
bool isSuccess;

do 
{
    Console.Write("Enter a number between 1 and 10: ");
    string input = Console.ReadLine();
    isSuccess = int.TryParse(input, out validNumber);
} while (!isSuccess || validNumber < 1 || validNumber > 10);

Console.WriteLine($"Thank you for entering {validNumber}");

Case B: Filtering a List

Using foreach and if to process specific items.

var employees = new List<string> { "Alice_Dev", "Bob_HR", "Charlie_Dev", "Dave_Manager" };
var developers = new List<string>();

foreach (var emp in employees) 
{
    if (emp.EndsWith("_Dev")) 
    {
        developers.Add(emp.Replace("_Dev", ""));
    }
}

Case C: Simple Menu System

Using switch and while for a CLI menu.

bool running = true;
while (running) 
{
    Console.WriteLine("\n1. Start\n2. Settings\n3. Exit");
    string choice = Console.ReadLine();

    switch (choice) 
    {
        case "1": Console.WriteLine("Game Starting..."); break;
        case "2": Console.WriteLine("Settings Menu..."); break;
        case "3": running = false; break;
        default: Console.WriteLine("Invalid choice."); break;
    }
}