C# & .NET Framework The .NET Grand Finale
💡
Exercise 49

Interview Prep — The Architect's Final Blueprint 25 XP Hard

Ctrl+Enter Run Ctrl+S Save

🎓 C#/.NET Interview Masterclass

You've built the entire .NET city from the ground up. Now it's time to walk into that interview room and own it. This lesson covers the most-asked, highest-impact concepts that senior interviewers love to probe.

🏗️ SOLID Principles

The five pillars of maintainable object-oriented design:

  • S — Single Responsibility: A class should have one, and only one, reason to change.
  • O — Open/Closed: Open for extension, closed for modification. Use inheritance/interfaces to add behavior.
  • L — Liskov Substitution: Subtypes must be substitutable for their base types without breaking correctness.
  • I — Interface Segregation: Many small, specific interfaces are better than one large, general-purpose one.
  • D — Dependency Inversion: Depend on abstractions (interfaces), not concrete implementations.

🎨 Essential Design Patterns

Singleton Pattern

Ensures a class has exactly one instance and provides a global point of access. Common use: configuration managers, logging services, connection pools.

public sealed class Singleton { private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>(() => new Singleton()); private Singleton() { Console.WriteLine("Singleton instance created"); } public static Singleton Instance => _instance.Value; }

Factory Pattern

Creates objects without exposing creation logic. The caller works with an interface, and the factory decides which concrete class to instantiate.

interface IBuilding { string Name { get; } } class Office : IBuilding { public string Name => "Office"; } class Residence : IBuilding { public string Name => "Residence"; } class BuildingFactory { public static IBuilding Create(string type) => type switch { "office" => new Office(), "residence" => new Residence(), _ => throw new ArgumentException($"Unknown type: {type}") }; }

Observer Pattern

Defines a one-to-many dependency: when one object changes state, all dependents are notified. In C#, event and delegate are built-in observer mechanisms.

class CityAlert { public event Action<string> OnAlert; public void RaiseAlert(string msg) => OnAlert?.Invoke(msg); } // Subscribe var alert = new CityAlert(); alert.OnAlert += msg => Console.WriteLine($"Fire dept: {msg}"); alert.OnAlert += msg => Console.WriteLine($"Police: {msg}"); alert.RaiseAlert("Emergency!");

💉 Dependency Injection (DI)

Instead of a class creating its own dependencies, they are injected from outside (usually via constructor). This makes code testable, flexible, and follows the Dependency Inversion Principle.

interface ILogger { void Log(string msg); } class ConsoleLogger : ILogger { public void Log(string msg) => Console.WriteLine(msg); } class BuildingService { private readonly ILogger _logger; public BuildingService(ILogger logger) => _logger = logger; public void Build() => _logger.Log("Building constructed!"); } // Injection var service = new BuildingService(new ConsoleLogger()); service.Build();

⚡ Async Best Practices

  • Always use async Task, never async void (except event handlers).
  • Use ConfigureAwait(false) in library code to avoid deadlocks.
  • Prefer ValueTask for hot paths where the result is often synchronous.
  • Use CancellationToken to support cooperative cancellation.
  • Avoid .Result and .Wait() — they block and can deadlock.

🧠 Memory Management

  • Stack: Fast, LIFO. Stores value types and method frames. Auto-cleaned when scope exits.
  • Heap: Managed by GC. Stores reference type instances. Three generations (0, 1, 2) + LOH.
  • Span<T>: Stack-only type for slicing arrays/memory without allocations.
  • ArrayPool<T>: Rent and return arrays to avoid GC pressure.
  • IDisposable: Deterministic cleanup of unmanaged resources.

🧵 Threading Essentials

  • Thread — OS-level thread. Heavyweight. Avoid creating manually in modern C#.
  • ThreadPool — Reuses threads. Task.Run queues work to the pool.
  • lock — Mutual exclusion. Uses Monitor under the hood.
  • SemaphoreSlim — Limits concurrent access to a resource (async-friendly).
  • ConcurrentDictionary<K,V> — Thread-safe dictionary without explicit locking.
  • Interlocked — Atomic operations (increment, compare-and-swap) without locks.

📝 Quick-Fire Interview Answers

  • struct vs class? — Struct is a value type (stack), class is a reference type (heap). Structs are copied on assignment.
  • abstract vs interface? — Abstract classes can have implementation; interfaces (pre-C# 8) are pure contracts. C# 8+ allows default interface methods.
  • sealed keyword? — Prevents inheritance. Also optimizes virtual dispatch (JIT can devirtualize calls).
  • ref vs out? — Both pass by reference. out must be assigned inside the method; ref must be initialized before the call.
  • string vs StringBuilder? — Strings are immutable; concatenation creates new objects. Use StringBuilder for many concatenations.
📋 Instructions
**The Architect's Singleton Blueprint** Demonstrate the Singleton pattern: 1. Create a `sealed class CityManager` with: - A `private static CityManager _instance` field (initially null) - A private constructor that prints `"Singleton instance created"` - A public static `Instance` property that creates the instance on first access (lazy initialization) 2. In `Main`: - Access `CityManager.Instance` and store in `mgr1` - Access `CityManager.Instance` again and store in `mgr2` - Print `"Same instance: {result}"` where result is `mgr1 == mgr2` (should be `True`) - Print `"Pattern: Singleton ensures one instance"` - Print `"Ready for interviews!"`
Make the constructor private and print the creation message. The static Instance property should check if _instance is null, and if so, create it. Both mgr1 and mgr2 will point to the same object, so `mgr1 == mgr2` evaluates to True.
main.py
Hi! I'm Rex 👋
Output
Ready. Press ▶ Run or Ctrl+Enter.