You've built decision trees, routed materials, secured the city, and inspected buildings. Now it's time for the Knowledge Inspection — 15 challenging questions that test your deep understanding of C# control flow.
These aren't surface-level questions, Architect. They're the kind you'd face in a senior C# developer interview. Think carefully, consider edge cases, and remember what you've learned!
📋 Instructions
🧠 Quiz Time
0 / 15 answered
1
In C#, which statement about if/else vs switch performance is correct?
A) switch is always faster than if/else regardless of the number of cases
B) The compiler may optimize a switch on integers into a jump table, making it O(1) lookup, while a long if/else chain is O(n)
C) if/else is always faster because the compiler inlines each comparison
D) switch and if/else always compile to identical IL code
When switching on integers or enums with contiguous values, the C# compiler (and JIT) can emit a jump table — an array of branch targets indexed by the switch value — giving O(1) dispatch. An if/else chain evaluates conditions sequentially (O(n) in the worst case). However, for small case counts or sparse values, the compiler may emit sequential comparisons for both, so switch isn't *always* faster.
2
What is the key difference between a switch statement and a switch expression in C#?
A) Switch expressions can only be used with string types
B) Switch statements return a value; switch expressions execute code blocks
C) Switch expressions return a value and use '=>' arms; switch statements execute code blocks using 'case'/'break'
D) Switch expressions were introduced in C# 6 and are now deprecated
Switch expressions (introduced in C# 8) are designed to return a value using '=>' (lambda arrow) syntax. They're concise and expression-based. Switch statements use 'case'/'break' and execute imperative code blocks. Switch expressions throw SwitchExpressionException if no arm matches (and there's no discard '_'), while switch statements silently fall through to the end if no default exists.
3
Which C# version introduced type patterns and the 'when' clause in switch statements?
A) C# 6.0
B) C# 7.0
C) C# 8.0
D) C# 9.0
C# 7.0 was the landmark release for pattern matching. It introduced type patterns (case int x:), constant patterns, var patterns, and the 'when' clause for additional guard conditions in switch cases. C# 8 added switch expressions and property patterns. C# 9 added relational and logical patterns (and, or, not).
4
What does the following code print?
bool a = false;
bool b = true;
int x = 0;
if (a && (++x > 0))
Console.Write("A");
if (b || (++x > 0))
Console.Write("B");
Console.Write(x);
A) AB2
B) B0
C) AB0
D) B1
Short-circuit evaluation is the key. In the first if: 'a' is false, so the right side (++x) is NEVER evaluated — x stays 0. The condition is false, so 'A' is not printed. In the second if: 'b' is true, so the right side (++x) is NEVER evaluated — x stays 0. The condition is true, so 'B' IS printed. Finally, x is printed as 0. Output: B0.
5
Which statement about the ternary operator (? :) in C# is TRUE?
A) Ternary expressions can be used as statements without assigning the result
B) The two result expressions must be of the same type (or implicitly convertible to a common type)
C) Ternary operators cannot be nested inside other ternary operators
D) The ternary operator evaluates both branches before returning one
The ternary operator requires both branches to yield the same type (or types that share an implicit conversion to a common type). In C#, the ternary is an expression, not a statement — you can't use it standalone without assigning or passing the result (unlike some other languages). Nesting IS allowed (though discouraged for readability). Only one branch is ever evaluated, not both.
6
What does the null-coalescing operator (??) do, and what does this expression return?
string name = null;
string result = name ?? "Unknown";
A) It checks if 'name' is empty and returns "Unknown" if so — result is "Unknown"
B) It returns the left operand if it is non-null; otherwise it returns the right operand — result is "Unknown"
C) It throws a NullReferenceException because name is null
D) It assigns "Unknown" to name and then to result — both become "Unknown"
The null-coalescing operator (??) returns its left operand if it is non-null; otherwise, it evaluates and returns its right operand. Since 'name' is null, result gets "Unknown". Note: ?? checks for null specifically, NOT for empty strings. Also, 'name' itself remains null — ?? does not modify the left operand (that's what ??= does).
7
What does the null-conditional operator (?.) do in this code?
string name = null;
int? length = name?.Length;
A) It throws a NullReferenceException because name is null
B) It sets length to 0 because an empty string has Length 0
C) It short-circuits the member access: if name is null, the entire expression evaluates to null instead of throwing
D) It converts the string to a nullable string type
The null-conditional operator (?.) safely accesses members on potentially null objects. If 'name' is null, instead of throwing NullReferenceException, the expression name?.Length evaluates to null. The result must be assigned to a nullable type (int?) since it might be null. This is part of C#'s null-safety toolkit alongside ??, ??=, and nullable reference types.
8
What is the difference between 'is' and 'as' keywords in C#?
A) 'is' performs a cast and returns the result; 'as' returns a boolean
B) 'is' returns a bool (and optionally declares a typed variable in C# 7+); 'as' attempts a cast and returns null on failure (reference types only)
C) 'as' can be used with value types; 'is' cannot
D) They are identical — 'as' is just an alias for 'is'
'is' checks type compatibility and returns bool. Since C# 7, 'is' can also declare a pattern variable: if (obj is string s). 'as' attempts a cast and returns null if the cast fails — but it only works with reference types and nullable value types (it can't return null for non-nullable value types like int). Modern C# favors 'is' with pattern matching over 'as' because it's safer and more expressive.
9
In a switch statement, what does the 'when' clause do?
case int n when n > 100:
Console.WriteLine("Large number");
break;
A) It replaces the 'break' statement and prevents fall-through
B) It adds additional boolean guard conditions to a case — the case matches only if the pattern AND the when condition are both true
C) It delays execution of the case block until a condition becomes true at runtime
D) It is syntactic sugar for an if statement inside the case block
The 'when' clause (introduced in C# 7) acts as a guard condition on a case. The case only matches if BOTH the type/value pattern matches AND the 'when' condition evaluates to true. This allows fine-grained filtering without nesting if statements inside case blocks. If the 'when' fails, the switch continues checking subsequent cases.
10
Why does C# allow 'goto case' and 'goto default' in switch statements?
A) It's a legacy feature from C that has no use in modern C#
B) Because C# prohibits implicit fall-through, 'goto case' provides explicit, intentional fall-through when one case needs to execute another case's logic
C) It's used to exit the switch statement early, similar to 'break'
D) It enables recursive switch patterns where cases call themselves
C# forbids implicit fall-through (a case with code MUST end with break, return, throw, or a jump statement). But sometimes you genuinely want one case to flow into another. 'goto case X' and 'goto default' provide this explicitly — making the developer's intent crystal clear. It's not a legacy artifact; it's a deliberate design choice to make fall-through opt-in rather than accidental.
11
Given this truth table scenario, what does the expression evaluate to?
bool p = true, q = false;
bool result = !(p && q) || (p && !q);
A) false
B) true
C) Compile error — cannot mix && and || without parentheses
D) It depends on the order of evaluation
Step by step: p && q = true && false = false. !(false) = true. p && !q = true && !false = true && true = true. true || true = true. The result is true. Note: even with short-circuit evaluation, the result is deterministic. The left side of || is true, so the right side (p && !q) is never evaluated — but the final result is still true regardless.
12
What is conditional compilation in C#, and which directive is used?
A) Using if/else to compile code differently — controlled by #if, #elif, #else, #endif preprocessor directives
B) Using switch statements that are resolved at compile time
C) A feature of the .NET runtime that interprets code differently based on platform
D) A NuGet package that provides compile-time code generation
C# supports preprocessor directives (#if, #elif, #else, #endif) for conditional compilation. Code inside a false branch is completely excluded from compilation — it doesn't exist in the output IL. Common symbols include DEBUG, RELEASE, and custom symbols defined via #define or project settings. The [Conditional("DEBUG")] attribute also enables conditional method inclusion.
13
In C# 8+ with nullable reference types enabled, what happens here?
#nullable enable
string? name = null;
int length = name.Length;
A) It compiles without warnings and 'length' is 0
B) The compiler emits a warning (CS8602): 'name' may be null, and at runtime it throws NullReferenceException
C) It won't compile — accessing .Length on a nullable string is a hard error
D) The compiler automatically inserts a null check and sets length to -1
With nullable reference types enabled (#nullable enable), the compiler performs static flow analysis. It sees that 'name' is declared as 'string?' (nullable) and never null-checked, so accessing .Length generates warning CS8602 ('Dereference of a possibly null reference'). However, it's a WARNING, not an error — the code still compiles. At runtime, since name IS actually null, it throws NullReferenceException. Use name?.Length or a null check to fix it.
14
Is a 'default' case required in a C# switch statement?
A) Yes — a switch statement without default will not compile
B) No — default is optional in switch statements, but switch EXPRESSIONS require exhaustive matching (all possible values must be handled, usually via '_' discard)
C) Default is required in both switch statements and switch expressions
D) Default is never required — the compiler ignores unmatched values in all cases
In a switch statement, default is optional. If no case matches and there's no default, execution simply continues past the switch. In a switch expression (C# 8+), the compiler warns if the expression isn't exhaustive — it must handle all possible values. If a switch expression fails to match at runtime, it throws SwitchExpressionException. Using the discard '_' pattern as the last arm ensures exhaustiveness.
15
How has pattern matching evolved across C# versions? Which statement is INCORRECT?
A) C# 7 introduced type patterns, constant patterns, and the 'when' clause
B) C# 8 introduced switch expressions, property patterns, and tuple patterns
C) C# 9 introduced relational patterns (< > <= >=), logical patterns (and, or, not), and simple type patterns
D) C# 9 introduced list patterns that can match arrays with [1, 2, .., 5] syntax
List patterns ([1, 2, .., 5]) were introduced in C# 11, NOT C# 9. The evolution is: C# 7 → type/constant/var patterns + when clause; C# 8 → switch expressions + property/tuple/positional patterns; C# 9 → relational + logical patterns (and/or/not) + simple type patterns; C# 10 → extended property patterns; C# 11 → list patterns + slice patterns. Each version made pattern matching increasingly powerful.