You've mastered generics, collections, LINQ, lambdas, and delegates — now it's time to prove your expertise, Architect! These 15 interview-grade questions cover deep internals, performance characteristics, and advanced patterns that separate senior developers from the rest. Think carefully before answering!
📋 Instructions
🧠 Quiz Time
0 / 15 answered
1
What is the key performance difference between List<T> and LinkedList<T> for inserting an element at the beginning of the collection?
A. List<T> is O(1) and LinkedList<T> is O(n)
B. List<T> is O(n) and LinkedList<T> is O(1)
C. Both are O(1)
D. Both are O(n)
List<T> is backed by an array, so inserting at position 0 requires shifting all existing elements, making it O(n). LinkedList<T> uses a doubly-linked list where inserting at the head only requires updating a couple of pointers, making it O(1).
2
How does Dictionary<TKey, TValue> handle hash collisions internally in .NET?
A. It uses open addressing with linear probing
B. It uses separate chaining with linked lists via bucket arrays
C. It rehashes using a secondary hash function
D. It throws an exception when a collision occurs
The .NET Dictionary<TKey, TValue> uses separate chaining. Each bucket index can hold a chain of entries stored in an internal entries array, linked together via a 'next' field. When a collision occurs, the new entry is prepended to the chain at that bucket index.
3
Which interface in the hierarchy provides Count and Add/Remove capabilities but NOT index-based access?
A. IEnumerable<T>
B. ICollection<T>
C. IList<T>
D. IReadOnlyList<T>
IEnumerable<T> only provides enumeration (GetEnumerator). ICollection<T> extends it with Count, Add, Remove, Contains, and Clear — but no indexer. IList<T> extends ICollection<T> and adds index-based access via this[int index], Insert, and RemoveAt.
4
Which of the following LINQ operations causes IMMEDIATE execution rather than deferred execution?
A. Where(x => x > 5)
B. Select(x => x * 2)
C. OrderBy(x => x)
D. ToList()
Where, Select, and OrderBy all use deferred execution — they return an IEnumerable<T> that isn't evaluated until iterated. ToList() forces immediate execution by iterating through the entire sequence and storing results in a new List<T>. Other immediate operators include ToArray(), Count(), First(), Sum(), and ToDictionary().
5
What does the 'yield return' keyword do in a C# iterator method?
A. Returns the final result and terminates the method
B. Pauses execution, returns the current value, and resumes where it left off on the next iteration
C. Creates a new thread to yield execution to another process
D. Throws a YieldException that must be caught by the caller
yield return implements a state machine under the hood. When the caller requests the next element, execution resumes right after the yield return statement. The method's local variables and execution position are preserved between calls. This enables lazy evaluation — elements are produced one at a time, on demand.
6
Given `IEnumerable<string> strings = new List<string> { "hello" };` and a method accepting `IEnumerable<object>`, will the assignment compile in C# 4.0+?
A. No, because IEnumerable<T> is invariant
B. Yes, because IEnumerable<T> is covariant (declared with 'out T')
C. Only if you explicitly cast with (IEnumerable<object>)
D. Only with a contravariant generic constraint
IEnumerable<out T> is covariant — the 'out' keyword means T only appears in output positions. Since string derives from object, IEnumerable<string> can be used wherever IEnumerable<object> is expected. This was introduced in C# 4.0. In contrast, Action<in T> is contravariant — T only appears in input positions.
7
What makes ConcurrentDictionary<TKey, TValue> thread-safe compared to a regular Dictionary with manual locking?
A. It locks the entire dictionary for every operation
B. It uses fine-grained striped locking on bucket segments, not the entire dictionary
C. It creates a copy of the dictionary for each thread
D. It uses immutable data structures internally
ConcurrentDictionary uses fine-grained locking (lock striping) where the internal buckets are divided into segments, each with its own lock. This allows multiple threads to read and write to different segments simultaneously. It also uses lock-free reads in .NET Core for even better read performance. This is far more efficient than locking the entire dictionary.
8
What is the fundamental difference between an Expression<Func<T, bool>> and a Func<T, bool>?
A. Expression trees can only be used with LINQ to Objects
B. A Func is compiled IL code; an Expression is a data structure describing the code that can be inspected and translated
C. Expression trees execute faster than compiled delegates
D. There is no practical difference; they are interchangeable
A Func<T, bool> is a compiled delegate — executable code. An Expression<Func<T, bool>> is an expression tree — a data structure (abstract syntax tree) that represents the code's logic. LINQ providers like Entity Framework inspect and translate expression trees into SQL. This is why IQueryable<T> works with Expression<> while IEnumerable<T> works with Func<>.
9
What is the critical difference between IQueryable<T> and IEnumerable<T> when querying a database?
A. IQueryable<T> loads all records into memory first, then filters
B. IEnumerable<T> translates the query to SQL for server-side execution
C. IQueryable<T> builds an expression tree that is translated to SQL and executed on the database server
D. Both execute identically but IQueryable<T> is just a newer API
IQueryable<T> composes queries as expression trees. When executed, the LINQ provider (e.g., Entity Framework) translates the expression tree into SQL, executing the filter/sort/join on the database server. IEnumerable<T> pulls ALL records into memory and filters in-process using C# delegates. Using IEnumerable<T> on a large table could load millions of rows unnecessarily.
10
In LINQ to Objects, calling .Where().Select().OrderBy() creates a chain of iterators. When does the actual sorting occur?
A. Immediately when OrderBy is called
B. When the first element is requested from the chain, OrderBy must consume the entire input before yielding any element
C. Elements are sorted one at a time as they are requested
D. The sort is optimized away and elements are yielded in original order
OrderBy is a 'buffering' operator — it uses deferred execution (it doesn't start until iterated), but once started, it must consume ALL elements from its input source to sort them before it can yield the first result. This is unlike Where and Select which can stream elements one at a time. This makes OrderBy O(n log n) and requires buffering the entire sequence.
11
What does the generic constraint 'where T : class, new()' mean?
A. T must be a value type with a parameterless constructor
B. T must be a reference type with a public parameterless constructor
C. T must inherit from a class named 'new'
D. T must be a reference type that is immutable
The 'class' constraint restricts T to reference types (classes, interfaces, delegates, arrays). The 'new()' constraint requires T to have a public parameterless constructor, allowing the generic code to create instances with 'new T()'. The new() constraint must appear last in the constraint list. Note: 'struct' would constrain to value types and implicitly includes a parameterless constructor.
12
What happens when you modify an ObservableCollection<T> from a background thread in a WPF application?
A. It works fine because ObservableCollection<T> is thread-safe
B. It silently drops the notification event
C. It throws an InvalidOperationException because the CollectionChanged event must be raised on the UI thread
D. It automatically marshals the call to the UI thread
ObservableCollection<T> is NOT thread-safe. Its CollectionChanged event is raised on the calling thread, and WPF's binding system requires that collection changes are made on the UI dispatcher thread. Modifying from a background thread throws an InvalidOperationException. Solutions include using Dispatcher.Invoke, BindingOperations.EnableCollectionSynchronization, or a thread-safe wrapper.
13
What is the purpose of ReadOnlyCollection<T> and how does it relate to the underlying collection?
A. It creates a deep copy of the collection that cannot be modified
B. It wraps an existing IList<T> and prevents direct modification, but changes to the underlying list ARE reflected
C. It creates an immutable snapshot that is completely independent of the source
D. It freezes the underlying collection so neither the wrapper nor the original can be modified
ReadOnlyCollection<T> is a thin wrapper around an existing IList<T>. It hides mutating methods (Add, Remove, etc.) but does NOT copy the data. If the underlying list is modified, those changes are visible through the ReadOnlyCollection. For a truly immutable collection, use ImmutableList<T> from System.Collections.Immutable, which creates structural copies on modification.
14
You need a custom equality comparison for Dictionary keys. Which interface should you implement and pass to the Dictionary constructor?
A. IComparable<T>
B. IEquatable<T>
C. IEqualityComparer<T>
D. IComparer<T>
IEqualityComparer<T> defines Equals(T, T) and GetHashCode(T) for external comparison logic and is passed to the Dictionary constructor: new Dictionary<K,V>(comparer). IEquatable<T> is implemented BY the type itself. IComparable<T> and IComparer<T> are for ordering/sorting, not equality-based lookups. A poorly implemented GetHashCode can degrade Dictionary performance from O(1) to O(n).
15
What is the key difference between Func<T, TResult>, Action<T>, and Predicate<T>?
A. Func always takes exactly one parameter, Action takes none, and Predicate takes two
B. Func returns a value (last type parameter is return type), Action returns void, and Predicate always returns bool
C. They are all identical but from different namespaces
D. Func and Action are delegates but Predicate is an interface
Func<T1, ..., TResult> can take 0-16 input parameters and always returns a value (the last type parameter). Action<T1, ...> can take 0-16 parameters but returns void — used for side effects. Predicate<T> takes exactly one parameter and returns bool — it's essentially Func<T, bool> but predates it and is used by List<T>.Find/FindAll/Exists. All three are delegate types in System namespace.