GDKB

Conditional Statements

Beginner15 Minutes readUpdated July 21, 20261 related concept

A conditional statement runs one block of code or another depending on whether a boolean expression is true or false. `if`, `else`, and `switch` are the standard forms. Conditionals are how a program branches — how "the door opens if the player has the key" becomes something a computer actually decides at runtime, instead of always doing the same thing.

On this page

Conditional Statements

Summary

A conditional statement runs one block of code or another depending on whether a boolean expression is true or false. if, else, and switch are the standard forms. Conditionals are how a program branches — how "the door opens if the player has the key" becomes something a computer actually decides at runtime, instead of always doing the same thing.

Mental Model

A conditional is a fork in a road, with a sign at the fork that reads the result of a boolean expression: if the sign says "true," take the left path; otherwise take the right. Only one path is ever taken per visit to the fork — the paths are mutually exclusive, which is precisely what distinguishes an if/else from just running both blocks in sequence.

Chained conditions (if / else if / else) are a sequence of forks: check the first sign; if it doesn't say true, move to the next fork and check that one; keep going until one says true, or reach the final, unconditional path (else) if none did.

Core Explanation

  • if runs a block only when its condition (a boolean expression, see Expressions) is true. Nothing runs, and execution simply continues past the block, when it is false.
  • if / else adds a second block that runs exactly when the condition is false — the two blocks are mutually exclusive by construction; exactly one of them executes.
  • if / else if / else chains conditions: each else if's condition is only checked if every condition before it was false. The first one that evaluates true runs its block, and every condition after it is skipped — order matters, and a more specific condition placed after a more general one that would also match it will never be reached.
  • switch compares one expression's value against a fixed set of possible values (often an enum), running the matching case's block. It expresses "one of several known, discrete options" more directly than a long if/else if chain testing the same variable repeatedly, and in many languages allows the compiler to warn when a case is missing (exhaustiveness checking on an enum).
  • The ternary conditional expression (condition ? a : b) is not a statement — it's an expression (see Expressions) that evaluates to a or b directly, useful for a single value choice inline (string label = isAlive ? "Alive" : "Dead";) rather than branching execution of a whole block.

Nesting. A conditional's block can contain another conditional. Deep nesting (an if inside an if inside an if) is legal but quickly becomes hard to read; restructuring nested conditions into a flatter chain, or extracting a named boolean expression, usually improves clarity without changing behavior.

Guard clauses. Checking an invalid or edge case first and returning (or otherwise exiting) early, rather than nesting the "normal" logic inside an if (isValid), keeps the common case at the outer indentation level instead of buried inside a condition — a widely used structuring pattern once Functions makes "return early" available.

Syntax / Examples

C# (Unity)

csharp
if (health <= 0)
{
    Die();
}
else if (health < lowHealthThreshold)
{
    PlayLowHealthWarning();
}
else
{
    PlayIdleAnimation();
}

switch (currentState)
{
    case PlayerState.Idle:   PlayIdle();   break;
    case PlayerState.Moving: PlayMoving();  break;
    case PlayerState.Attacking: PlayAttack(); break;
    default: PlayIdle(); break;
}

string label = isAlive ? "Alive" : "Dead"; // ternary expression

C++ (Unreal)

cpp
if (Health <= 0)
{
    Die();
}
else if (Health < LowHealthThreshold)
{
    PlayLowHealthWarning();
}
else
{
    PlayIdleAnimation();
}

switch (CurrentState)
{
    case EPlayerState::Idle:      PlayIdle();   break;
    case EPlayerState::Moving:    PlayMoving();  break;
    case EPlayerState::Attacking: PlayAttack(); break;
    default:                      PlayIdle();   break;
}

Unreal Blueprint

The Branch node is the visual if/else: one input (a boolean), two output execution pins (True, False). Chained conditions are expressed as a Branch feeding into another Branch on its False pin, mirroring else if. A Switch on Enum node (or Switch on Int/String) mirrors switch, with one execution output pin per case.

Practical Example

Deciding a monster's action each turn is a natural fit for a chained conditional, checked in priority order from most to least urgent:

csharp
if (health < fleeThreshold)
{
    Flee();
}
else if (Vector3.Distance(transform.position, player.position) <= attackRange)
{
    Attack();
}
else if (CanSeePlayer())
{
    Chase();
}
else
{
    Patrol();
}

Order encodes priority here: fleeing is checked first because it should override attacking even if the player happens to be in range — reordering these conditions would change the monster's behavior even though each individual condition is unchanged.

Common Mistakes

  • Dangling else ambiguity avoided by always using braces — omitting braces on a single-statement if invites a later edit to add a second statement that silently falls outside the conditional.
  • Order-dependent else if chains authored as if order didn't matter — placing a general condition before a more specific one that it also satisfies means the specific branch is unreachable; the fix is ordering from most specific/urgent to least, as in the Practical Example.
  • Using a chain of ifs (not else if) when the conditions are mutually exclusive alternatives — every condition gets evaluated even after an earlier one matched, which is both wasted work and, if the conditions aren't actually mutually exclusive, a source of double-execution bugs.
  • Forgetting a default case in a switch — an unhandled value falls through silently unless a default exists to catch it (or the language is configured to error on non-exhaustive matches).
  • Deeply nested conditionals that could be flattened with guard clauses or early returns, making the common path easier to find by eye.

Best Practices

  • Always use braces, even for a single-statement block.
  • Order else if branches from most specific/urgent to least, when they could otherwise overlap.
  • Prefer switch over a long if/else if chain when comparing one value against several discrete known options, especially an enum.
  • Use guard clauses / early returns to avoid nesting the common case inside a validity check.
  • Reach for the ternary expression only for a single inline value choice, not as a substitute for a full conditional with real branching logic.

Exercises

  1. Convert a three-level nested if into an equivalent flat chain of guard clauses, and confirm the behavior is unchanged.
  2. Write a switch over a Direction enum (Up, Down, Left, Right) that returns the corresponding movement vector, including a default case.
  3. Given the monster-action example, explain what would change in behavior if the Flee and Attack branches were swapped.
  4. Rewrite a chain of independent if statements (that should have been mutually exclusive) as a proper else if chain, and identify the bug the original version could cause.
  • Expressions — every condition is a boolean expression.
  • Loops — repeatedly re-evaluates a condition, rather than branching once.

References

Related Concepts