Advanced C# Interview: How to Impress an Employer and Not Fail
\n\nAn interview for a Senior or Middle .NET developer position is not just a syntax check. Interviewers want to see a deep understanding of the platform's internal workings, the ability to write performant code, and make architectural decisions. In this article, we will break down key topics and advanced-level questions that will help you pass a technical C# interview.
\n\n1. Asynchronous Programming: async/await and Task
\n\nThis is perhaps the most popular topic in interviews. It's not enough to say you know async/await. You need to understand how it works under the hood.
Question: What is SynchronizationContext and how does it affect the execution of await?
\nA classic question. SynchronizationContext is an abstraction that allows scheduling code execution in a specific context (e.g., in a WPF UI thread or in an ASP.NET thread pool). When you use await, after the asynchronous operation completes, the code by default tries to return to the original context. In console applications, this is the thread pool, and in UI applications, it's the dispatcher thread. Understanding this mechanism helps avoid deadlocks.
Question: What is the difference between Task.Run and Task.Factory.StartNew?
\nAt first glance, they are similar, but Task.Run is a simplified wrapper. Task.Factory.StartNew gives more control (e.g., you can set TaskCreationOptions or TaskScheduler). However, by default, Task.Run uses TaskScheduler.Default (the thread pool), while Task.Factory.StartNew may use the current scheduler, which can sometimes lead to unexpected behavior.
// Example of a deadlock due to SynchronizationContext (in a UI application)\npublic async Task<string> GetDataAsync()\n{\n var result = await SomeAsyncMethod(); // captures context\n return result.ToString(); // tries to return to the UI thread\n}\n\n// If you call .Result or .Wait() on the task, a deadlock will occur\nvar data = GetDataAsync().Result; // BAD!\n\n\n2. Memory Management and the Garbage Collector (GC)
\n\nAn advanced developer must understand how .NET manages memory to avoid leaks and optimize performance.
\n\nQuestion: Explain GC generations (Gen 0, Gen 1, Gen 2) and the Large Object Heap (LOH).
\nGC in .NET uses a generational model. Objects first go into Gen 0 (collected frequently). If they survive a collection, they move to Gen 1, then to Gen 2. The LOH is intended for objects larger than 85,000 bytes. It is not compacted by default (prior to .NET 4.5.1), which can cause fragmentation. The question can be made more complex: "How does background garbage collection work?" or "What are GC modes: Workstation vs Server?"
\n\nQuestion: What is Finalization and how to avoid related problems?
\nFinalizers (~Finalizer()) slow down garbage collection because objects with finalizers go into the finalization queue and require two GC passes. The correct pattern is implementing IDisposable with the Dispose pattern.
public class ResourceHolder : IDisposable\n{\n private IntPtr _handle;\n private bool _disposed = false;\n\n public void Dispose()\n {\n Dispose(true);\n GC.SuppressFinalize(this);\n }\n\n protected virtual void Dispose(bool disposing)\n {\n if (!_disposed)\n {\n if (disposing)\n {\n // Release managed resources\n }\n // Release unmanaged resources (handle)\n _disposed = true;\n }\n }\n\n ~ResourceHolder()\n {\n Dispose(false);\n }\n}\n\n\n3. Delegates, Events, and LINQ: From Syntax to Understanding
\n\nThese topics are often