What is a record in C#, and how is it different from a class?
A
record(C# 9) is a class with generated value-based equality: two records are equal when the runtime type and all fields match. It also gets a readableToString(), deconstruction andwithcopies.public record Person(string Name, int Age); // positional record, init-only properties var a = new Person("Asha", 30); var b = new Person("Asha", 30); Console.WriteLine(a == b); // True (a class would print False) Console.WriteLine(a); // Person { Name = Asha, Age = 30 } var older = a with { Age = 31 }; // copy with one property changed var (name, age) = older; // deconstructionrecord struct(C# 10) is the value-type version. See records.What are switch expressions and pattern matching in modern C#?
A switch expression (C# 8) is a
switchthat returns a value, with patterns in its arms: type, property, relational andand/or/notpatterns (C# 9), and list patterns (C# 11).static string Classify(object o) => o switch { null => "null", int n when n < 0 => "negative int", int and (0 or 1) => "bit", int => "int", string { Length: 0 } => "empty string", string s => $"string of {s.Length}", _ => "something else" }; int[] nums = [1, 2, 3]; if (nums is [1, .., var last]) // list pattern, C# 11 Console.WriteLine(last); // 3Arms are tried top to bottom, so put specific patterns first. See switch expressions.
What are nullable reference types in C# 8?
With
<Nullable>enable</Nullable>,stringmeans "never null" andstring?means "may be null", and the compiler warns on possible null dereferences.#nullable enable string name = null; // warning CS8625: null literal to non-nullable type string? nick = null; // fine int len = nick.Length; // warning CS8602: dereference of a possibly null reference int ok = nick?.Length ?? 0; // fine string forced = nick!; // ! tells the compiler "trust me", no runtime checkIt is compile-time only: at run time a
stringcan still be null, for example after deserialization.What do
initandrequireddo on properties?init(C# 9) makes a property settable only during object creation;required(C# 11) forces callers to set it in the object initializer.public class Order { public required string Id { get; init; } public decimal Total { get; init; } } var o = new Order { Id = "A-17", Total = 99m }; // fine o.Total = 10m; // error: init-only property var bad = new Order { Total = 5m }; // error CS9035: Id is requiredTogether they give immutable objects without a long constructor.
What are top-level statements, global usings and file-scoped namespaces?
Top-level statements (C# 9) let
Program.cshold code without aclass ProgramandMain; global usings (C# 10) import a namespace for the whole project; file-scoped namespaces (C# 10) remove one level of braces.// Program.cs, the whole file (top-level statements) Console.WriteLine("Hello"); return 0; // GlobalUsings.cs global using System.Text.Json; // Customer.cs, file-scoped namespace namespace Shop.Models; public class Customer { }Only one file per project can have top-level statements.
What are primary constructors and collection expressions in C# 12?
C# 12 lets a class or struct declare constructor parameters next to its name (primary constructors) and build collections with brackets and the
..spread (collection expressions).public class OrderService(IOrderRepository repo, ILogger<OrderService> log) { public Order Get(int id) { log.LogInformation("Loading {Id}", id); return repo.Find(id); // parameters are in scope everywhere in the class } } int[] a = [1, 2, 3]; List<int> b = [0, .. a, 4]; // 0, 1, 2, 3, 4 Span<char> empty = [];Trap: in a class, those parameters do not become public properties as in a record; they are mutable captured variables.
What are
Span<T>andMemory<T>, and why do they matter for performance?Span<T>(C# 7.2) is a view over contiguous memory (an array, part of a string, stack memory) that slices without copying. It is aref structthat lives only on the stack;Memory<T>can be stored in fields and used acrossawait.ReadOnlySpan<char> line = "2026-10-09,ORDER,149.50"; int comma = line.IndexOf(','); ReadOnlySpan<char> date = line.Slice(0, comma); // no new string allocated decimal total = decimal.Parse(line.Slice(line.LastIndexOf(',') + 1)); Span<byte> buffer = stackalloc byte[256]; // stack memory, no GCA span cannot be a field of a class, be boxed, or be captured by a lambda.
What are default interface methods (C# 8)?
Since C# 8 (.NET Core 3.0 and later), an interface member can have a body that implementers get unless they write their own, so a library can add a member without breaking them.
public interface ILogger { void Log(string message); void LogError(string message) => Log("ERROR: " + message); // default body } class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine(message); } ILogger log = new ConsoleLogger(); log.LogError("disk full"); // works through the interface // new ConsoleLogger().LogError(...) does not compile: not a class memberThe default member is reachable only through the interface type, and it does not work on .NET Framework.
What do
^1and..mean in C#?They are indices and ranges (C# 8):
^ncounts from the end, soa[^1]is the last element, anda[start..end]takes a slice with the end excluded. Either side of..can be left out.int[] a = { 10, 20, 30, 40, 50 }; Console.WriteLine(a[^1]); // 50, the last element Console.WriteLine(string.Join(",", a[1..3])); // 20,30 (the end is excluded) Console.WriteLine(string.Join(",", a[^2..])); // 40,50 Console.WriteLine("interview"[..5]); // inter Span<int> view = a.AsSpan()[1..4]; // 20, 30, 40 without copyingOn an array or a string a range makes a copy; on a
Span<T>it slices without copying.^0is one past the end, soa[^0]throwsIndexOutOfRangeException. TheSystem.IndexandSystem.Rangetypes behind them need .NET Core 3.0 or later.What are async streams (
IAsyncEnumerable<T>andawait foreach)?Async streams (C# 8) combine
yield returnwithawait: the producer awaits between items, and the consumer handles each item as it arrives instead of waiting for a wholeTask<List<T>>.static async IAsyncEnumerable<int> ReadPagesAsync( [EnumeratorCancellation] CancellationToken token = default) { for (int page = 1; page <= 3; page++) { await Task.Delay(100, token); // stands in for a database or HTTP call yield return page; } } await foreach (int page in ReadPagesAsync()) Console.WriteLine($"page {page}"); // page 1, page 2, page 3, one every 100 msUse them for paged APIs, database cursors and streamed responses; EF Core's
AsAsyncEnumerable()returns one. For cancellation, mark the token parameter[EnumeratorCancellation]so a caller can pass a token withWithCancellation(token).
Modern C# Features Interview Questions
Records, patterns, nullable references, init and Span<T>; they need a modern SDK, so output is in comments.