You've built skyscrapers, run elevators, inspected buildings, and designed entire city grids — all powered by loops. Now it's time to face the Interview Gauntlet. These 15 questions cover the subtle, tricky, and deeply technical aspects of loops in C# that real interviewers love to ask. 🏆
Topics covered: for vs foreach performance, while vs do-while guarantees, loop variable scope, IEnumerable, collection modification during iteration, break/continue/return, infinite loops, loop unrolling, yield return, Parallel.For, LINQ vs loops, goto, the iterator pattern, Span<T>, and the classic closure trap.
📋 Instructions
🧠 Quiz Time
0 / 15 answered
1
What is the key performance difference between `for` and `foreach` when iterating over an array in C#?
A. `foreach` is always significantly slower because it creates an enumerator object on the heap
B. For arrays, the JIT compiler optimizes `foreach` to be essentially identical to `for` — both eliminate bounds checks
C. `for` is always faster because `foreach` copies each element into a new variable
D. `foreach` is faster because it uses pointer arithmetic internally
When iterating over arrays, the C# JIT compiler recognizes the foreach pattern and optimizes it to indexed access with bounds-check elimination, making it perform essentially the same as a `for` loop. The enumerator is optimized away for arrays.
2
What is guaranteed about a `do-while` loop that is NOT guaranteed about a `while` loop?
A. The do-while loop always terminates
B. The do-while loop body executes at least once regardless of the condition
C. The do-while loop checks the condition before each iteration
D. The do-while loop is always faster than while
A `do-while` loop evaluates its condition AFTER executing the body, guaranteeing at least one execution. A `while` loop checks BEFORE, so if the condition is false initially, the body never runs. This is the fundamental semantic difference.
3
What happens to the loop variable `i` after a `for` loop in C#?
```csharp
for (int i = 0; i < 5; i++) { }
Console.WriteLine(i);
```
A. It prints 5
B. It prints 4
C. Compile error — `i` is not accessible outside the for loop
D. It prints 0 because i is reset
In C#, a variable declared in the for loop's initialization (`int i = 0`) is scoped to the loop. It does not exist after the closing brace. This is different from some older languages like pre-ES6 JavaScript where `var` would leak. To use the value after the loop, declare `i` before the for statement.
4
What interface must a type implement to be used with `foreach` in C#?
A. `IComparable`
B. `IEnumerable` or `IEnumerable<T>` (or have a public `GetEnumerator()` method)
C. `ICollection`
D. `IList`
The `foreach` statement requires the type to either implement `IEnumerable`/`IEnumerable<T>` or simply have a public `GetEnumerator()` method that returns a type with `MoveNext()` and `Current` (duck-typing pattern). The compiler uses pattern matching, not strict interface checks.
5
What happens if you modify a `List<T>` while iterating over it with `foreach`?
A. The modification is silently ignored
B. The loop skips the modified element
C. An `InvalidOperationException` is thrown at runtime
D. A compile-time error prevents this
The List<T> enumerator tracks a version number. If the list is modified (add/remove) during foreach iteration, the version changes and the enumerator throws an `InvalidOperationException` with the message 'Collection was modified; enumeration operation may not complete.' Use a regular for loop or iterate over a copy if you need to modify.
6
In a nested loop, what does `break` do?
A. Exits all enclosing loops
B. Exits only the innermost enclosing loop
C. Exits the method entirely
D. Skips to the next iteration of the outer loop
`break` exits only the nearest enclosing loop (the innermost one). The outer loop continues normally. C# does not support labeled breaks (unlike Java). To break out of multiple loops, you can use a boolean flag, extract the loops into a method and use `return`, or (rarely) use `goto`.
7
Which of the following creates an infinite loop that the compiler can detect as intentional?
A. `while (true) { }`
B. `for (;;) { }`
C. Both A and B — the C# compiler recognizes both as intentional infinite loops
D. Neither — C# always warns about infinite loops
Both `while (true)` and `for (;;)` are recognized by the C# compiler as intentional infinite loops. The compiler won't flag unreachable code after them as a warning in the same way. Both are idiomatic C# patterns — `while (true)` is more common in C# codebases, while `for (;;)` is more common in C/C++ tradition.
8
What is loop unrolling in the context of C# / .NET JIT compilation?
A. Converting a for loop into a foreach loop automatically
B. An optimization where the JIT duplicates the loop body multiple times to reduce branch overhead and enable vectorization
C. Replacing all loops with recursion for better stack usage
D. Converting nested loops into a single flat loop
Loop unrolling is a JIT optimization where the loop body is duplicated multiple times in generated machine code, reducing the overhead of branch prediction and loop counter checks. The .NET JIT (especially RyuJIT) can apply this for small, tight loops. It can also enable SIMD vectorization by processing multiple elements per unrolled iteration.
9
What does `yield return` do inside a loop?
A. Immediately terminates the loop and returns the value
B. Pauses the method's execution, returns the value to the caller, and resumes from the same point on the next iteration request
C. Adds the value to an internal list and returns it all at once when the loop ends
D. Creates a new thread to return values asynchronously
`yield return` turns a method into an iterator (state machine). When called inside a loop, it pauses execution, returns the current value to the caller, and resumes from exactly where it left off on the next `MoveNext()` call. This enables lazy evaluation — values are produced one at a time, on demand, without building an entire collection in memory.
10
How does `Parallel.For` differ from a regular `for` loop?
A. It runs iterations on multiple threads from the ThreadPool, with no guaranteed execution order
B. It's syntactic sugar that compiles to the same code as a regular for loop
C. It always uses exactly one thread per iteration
D. It guarantees iterations run in ascending order but on separate threads
`Parallel.For` (from `System.Threading.Tasks`) distributes loop iterations across ThreadPool threads for parallel execution. The order of execution is NOT guaranteed — iteration 5 might run before iteration 2. It automatically partitions the work and manages thread scheduling. Use it only when iterations are independent (no shared mutable state without synchronization).
11
When comparing LINQ `.Where().Select()` to a `foreach` loop with an `if` statement, which statement is most accurate?
A. LINQ is always faster because it's compiled to optimal IL
B. The foreach loop with if is always faster because LINQ has overhead from delegate invocations and iterator allocations, but LINQ offers better readability and composability
C. They produce identical IL and have zero performance difference
D. LINQ automatically parallelizes the operations
LINQ introduces overhead from delegate allocation, enumerator creation, and virtual calls through the iterator chain. A hand-written foreach with if/manual operations is typically faster in tight loops. However, LINQ provides superior readability, composability, and maintainability. In most real-world scenarios, the LINQ overhead is negligible compared to I/O or database operations. Use LINQ for clarity; optimize with foreach only when profiling reveals a bottleneck.
12
What does `goto` do in the context of loops in C#, and is it valid?
A. `goto` was removed from C# — it's a compile error
B. `goto` is valid in C# and can jump to labeled statements, but should be used sparingly — one legitimate use is breaking out of deeply nested loops
C. `goto` only works inside switch statements in C#
D. `goto` in C# can only jump forward, never backward
C# does support `goto` with labeled statements. While generally discouraged, one accepted use case is breaking out of deeply nested loops where a boolean flag would be cumbersome. For example: `goto LoopEnd;` can jump to `LoopEnd:` outside nested loops. `goto` can also be used in switch statements (`goto case X`). It cannot jump into a different method or into a try block.
13
What is the Iterator pattern in C# and how does it relate to foreach?
A. A design pattern where a class implements `IIterator` to enable random access
B. A pattern where `IEnumerable<T>` provides `GetEnumerator()` returning `IEnumerator<T>` with `MoveNext()`, `Current`, and `Reset()` — foreach desugars to this pattern
C. A pattern that requires implementing `IComparable` to sort elements during iteration
D. A pattern exclusive to List<T> that other collections cannot use
The Iterator pattern in C# is implemented via `IEnumerable<T>` and `IEnumerator<T>`. `foreach` is syntactic sugar: the compiler transforms it into calls to `GetEnumerator()`, then a while loop calling `MoveNext()` and reading `Current`. The enumerator also implements `IDisposable`, so foreach generates a try-finally block to ensure proper disposal.
14
How can `Span<T>` improve loop performance over arrays?
A. `Span<T>` allocates elements on the heap for faster GC
B. `Span<T>` provides a stack-allocated view over contiguous memory, enabling bounds-check elimination and avoiding heap allocations when slicing
C. `Span<T>` automatically parallelizes loop iterations
D. `Span<T>` is slower than arrays but safer
`Span<T>` is a ref struct (stack-only) that represents a contiguous region of memory. When looping over a `Span<T>`, the JIT can aggressively eliminate bounds checks, and slicing a Span creates a new view without allocating heap memory. This is especially powerful in high-performance scenarios like parsing, serialization, and buffer processing where avoiding heap allocations reduces GC pressure.
15
What is the output of this code, and why is it a classic interview trap?
```csharp
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.Write(i + " "));
}
foreach (var action in actions) action();
```
A. `0 1 2 ` — each closure captures a different value of i
B. `3 3 3 ` — all closures capture the same variable i, which is 3 after the loop ends
C. `2 2 2 ` — closures capture the last iterated value
D. Compile error — lambdas cannot capture loop variables
This is the classic 'loop variable capture' trap. All three lambdas capture the SAME variable `i`, not its value at capture time. After the for loop completes, `i` is 3, so all three closures print 3. The fix is to create a local copy inside the loop: `int local = i; actions.Add(() => Console.Write(local + " "));`. Note: C# 5+ fixed this for `foreach` loop variables (each iteration gets its own copy), but `for` loop variables are still shared.