C#
05 / 07

Modern C# Features & Interview Questions

C#: Modern Features & Interview Questions

Records (C# 9+)

// Record — immutable reference type with value equality
public record User(int Id, string Name, string Email);

var alice = new User(1, "Alice", "alice@example.com");
var alice2 = new User(1, "Alice", "alice@example.com");
alice == alice2     // true — value equality (not reference)
alice.Equals(alice2)  // true

// With expression — create copy with modifications
var alice3 = alice with { Email = "newalice@example.com" };

// Record struct (value type, stack-allocated)
public record struct Point(double X, double Y);

// Positional records with custom members
public record Order(int Id, List<Item> Items)
{
    public decimal Total => Items.Sum(i => i.Price);
    public bool IsEmpty => !Items.Any();
}

// Deconstruction
var (id, name, email) = alice;

// Primary constructors (C# 12) — for classes too
public class Service(ILogger<Service> logger, IRepository repo)
{
    public async Task Process() => await repo.DoWork();  // logger, repo captured
}

Pattern Matching (C# 7–11)

// Type pattern
if (shape is Circle c)
    Console.WriteLine($"Circle: r={c.Radius}");

// Property pattern
if (user is { IsActive: true, Role: "admin" })
    GrantAccess();

// List pattern (C# 11)
if (numbers is [1, 2, ..var rest])
    Console.WriteLine($"Starts with 1, 2. Rest: {rest.Length} items");

// Switch expression with patterns
decimal Discount(Order order) => order switch
{
    { Total: > 1000, Customer.IsPremium: true } => 0.20m,
    { Total: > 500 }                            => 0.10m,
    { Items.Count: 0 }                          => 0m,
    _                                            => 0.05m,
};

// Relational patterns (C# 9)
string Grade(int score) => score switch
{
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    < 0   => throw new ArgumentException("Negative score"),
    _     => "F",
};

// Logical patterns
bool IsWorkday(DayOfWeek day) =>
    day is not (DayOfWeek.Saturday or DayOfWeek.Sunday);

Nullable Reference Types & Span

// Enable in .csproj: <Nullable>enable</Nullable>
// Now all reference types are non-nullable by default
string name = "Alice";    // cannot be null — compiler warns
string? nickname = null;  // explicitly nullable

// Null-forgiving operator (tell compiler "trust me, not null")
string guaranteed = GetValue()!;  // suppresses warning

// Required members (C# 11)
public class Config
{
    public required string ConnectionString { get; init; }  // must be set
    public required int MaxConnections { get; init; }
}

var config = new Config
{
    ConnectionString = "Server=...",
    MaxConnections = 100
};  // compiler error if required properties missing

// Span<T> and Memory<T> — zero-copy slicing
void ProcessLine(ReadOnlySpan<char> line)
{
    // Span — no heap allocation, can't be stored
    var trimmed = line.Trim();
    var parts = trimmed.IndexOf(':');
}

// stackalloc — stack allocation for small buffers
Span<int> buffer = stackalloc int[10];
for (int i = 0; i < buffer.Length; i++)
    buffer[i] = i * 2;

Interview Questions

  • What is the difference between class and struct? Class is a reference type (heap), struct is a value type (stack). Structs are copied on assignment. Use structs for small, immutable data (Point, Color). Classes for identity-based objects.

  • Explain boxing and unboxing. Boxing wraps a value type in an object (heap allocation). Unboxing extracts it. Avoid in hot paths — use generics (List<int> not ArrayList) to prevent boxing.

  • What is the difference between == and .Equals()? By default == compares references for classes; .Equals() can be overridden for value equality. Records override both for value equality. Always override both together.

  • What is IDisposable and the using statement? IDisposable.Dispose() releases unmanaged resources. using ensures Dispose() is called even if an exception occurs. using declaration (C# 8+): `using var conn = new SqlConnection(...)` — disposed at end of scope.

  • What is a delegate vs event? Delegate is a type-safe function pointer. Event wraps a delegate with access restrictions — outside classes can only += or -= not = (preventing replacement of all handlers).

  • Explain the difference between IEnumerable and IQueryable. IEnumerable pulls all data into memory then filters. IQueryable translates LINQ to SQL — filtering happens in the DB. Use IQueryable for EF Core to avoid loading unnecessary data.

  • What are expression trees? Data structures representing code as data. EF Core uses them to translate LINQ expressions to SQL. Created when assigning a lambda to Expression<Func<T, TResult>> instead of Func<T, TResult>.

  • What is the Task Parallel Library (TPL)? The foundation of async in .NET. Task represents an async operation. Task.Run() offloads work to the thread pool. async/await is syntactic sugar over TPL.

  • Difference between async Task and async void? async Task allows callers to await and catch exceptions. async void is fire-and-forget — exceptions go to the SynchronizationContext and can crash the app. Only use async void for event handlers.

  • What are extension methods? Static methods that appear as instance methods on a type. Defined in static classes with `this` as first parameter. LINQ is built entirely on extension methods on IEnumerable<T>.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free