102 C# Interview Questions and Answers (2026)

Blog / 102 C# Interview Questions and Answers (2026)
C#

C# isn't just enterprise plumbing anymore. In the AI era it's become a default for shipping fast APIs and AI/ML backends, so more teams run on it and interviewers now expect genuine fluency. Walk in shaky on the type system, memory model, or async, and someone better-prepared takes the offer.

This is your fix: 102 questions with tight, interview-ready answers and code where it actually helps. They're worked Junior to Mid to Senior, so you build from fundamentals up to the CLR, GC, LINQ, and concurrency deep cuts. Work through them and you won't be guessing in the room.

Q1.
What is the difference between const and readonly? When is the value of each evaluated?

Junior

const is a compile-time constant baked into callers, while readonly is a runtime constant assigned once at construction. Use const only for values that never change; use readonly when the value is fixed per instance but computed at runtime.

  • const:

    • Evaluated at compile time; must be a literal compile-time value.

    • Implicitly static; limited to primitives, string, and enums.

    • Value is inlined into consuming assemblies, so changing it requires recompiling all consumers (versioning hazard).

  • readonly:

    • Evaluated at runtime; assignable only in its declaration or a constructor.

    • Can be any type, including objects computed at startup.

    • Can be instance-level (different value per object) or static readonly.

    • Read by reference at runtime, so changing it only requires recompiling the defining assembly.

csharp

public class Config { public const int MaxRetries = 3; // compile-time, inlined public readonly DateTime Created; // runtime, per instance public static readonly Guid SessionId = Guid.NewGuid(); public Config() => Created = DateTime.UtcNow; // set in constructor }

Q2.
What is the difference between a value type and a reference type in C#?

Junior

A value type holds its data directly and is copied by value; a reference type holds a reference to data on the heap and is copied by reference. This difference governs assignment, equality, and how changes propagate.

  • Value types:

    • Include struct, enums, and primitives (int, bool, double).

    • Stored inline (stack or within the containing object); assignment copies the whole value.

    • Cannot be null unless wrapped as Nullable<T> (int?).

  • Reference types:

    • Include class, interface, arrays, delegates, and string.

    • Object lives on the heap; the variable holds a reference, and assignment copies the reference (both point to the same object).

    • Default value is null; managed by the garbage collector.

  • Practical consequence: Mutating a value-type copy doesn't affect the original; mutating an object through one reference is visible through all references to it.

Q3.
What is the difference between the stack and the heap, and what gets stored on each?

Junior

The stack is a fast, thread-local LIFO region for method calls and their local data, while the heap is a larger, shared region for objects with managed lifetimes. What goes where depends on type and context, not on the keyword used.

  • Stack:

    • Holds method call frames: local variables, parameters, return addresses.

    • Stores value-type locals and the references (pointers) to heap objects.

    • Allocation/deallocation is just moving a pointer; freed automatically when the method returns.

  • Heap:

    • Holds all reference-type objects (class instances, arrays) and the value-type fields contained inside them.

    • Managed by the garbage collector, which reclaims unreachable objects.

  • Nuance:

    • A value type isn't always on the stack: a struct field inside a class lives on the heap with its owner, and captured variables get hoisted to heap-allocated closures.

    • It's better to reason about lifetime and containment than to assume "value = stack, reference = heap."

Q4.
What is the difference between managed and unmanaged code?

Junior

Managed code runs under the control of the .NET runtime (the CLR), which provides services like garbage collection and type safety; unmanaged code runs directly on the OS with no runtime supervision and manages its own memory.

  • Managed code:

    • Compiled to IL (Intermediate Language) and executed by the CLR, which supplies garbage collection, exception handling, and type/memory safety.

    • Examples: C#, F#, VB.NET output.

  • Unmanaged code:

    • Compiled straight to native machine code (C, C++) and runs without the CLR; you allocate and free memory yourself.

    • No automatic garbage collection, so leaks and pointer errors are your responsibility.

  • Interop bridges the two:

    • P/Invoke (DllImport) and COM interop let managed code call unmanaged libraries.

    • Crossing the boundary needs marshalling and sometimes manual cleanup (e.g. IDisposable / Marshal).

Q5.
What is the Common Language Runtime (CLR), and what are its primary responsibilities?

Junior

The CLR is the .NET virtual machine (execution engine) that runs managed code: it loads assemblies, JIT-compiles IL to native code, and provides core runtime services like memory management and security.

  • Execution and compilation: Loads assemblies and uses the JIT to turn IL into native code at runtime.

  • Automatic memory management: The garbage collector allocates and reclaims managed heap memory, removing manual free calls.

  • Type safety and verification: Enforces the Common Type System (CTS) and verifies IL so code can't violate memory boundaries.

  • Other services: Structured exception handling, thread management, security, and interop with unmanaged code.

  • Language neutrality: Any CLS-compliant language (C#, F#, VB.NET) compiles to the same IL the CLR executes.

Q6.
What is the difference between C#, .NET, and the CLR (Common Language Runtime)?

Junior

They sit at different layers: C# is a programming language, .NET is the platform (runtime, libraries, tooling) you build and run on, and the CLR is the specific runtime engine within .NET that executes your compiled code.

  • C# (the language): A syntax and set of language rules; the compiler turns it into IL. It's one of several .NET languages.

  • .NET (the platform): The umbrella: the runtime, the Base Class Library, SDK, and tooling that let you write, build, and run apps.

  • CLR (the runtime engine): The execution component of .NET: JIT-compiles IL, runs garbage collection, enforces type safety.

  • How they relate: C# code compiles to IL, .NET packages and ships it, and the CLR executes it at runtime.

Q7.
What are the different access modifiers in C#, and what does protected internal specifically mean?

Junior

Access modifiers control the visibility of types and members. C# has six: public, private, protected, internal, protected internal, and private protected. The two combined modifiers are the ones most often misunderstood.

  • The simple ones:

    • public: accessible from anywhere.

    • private: accessible only within the containing type (default for class members).

    • protected: accessible within the type and its derived types.

    • internal: accessible anywhere within the same assembly.

  • protected internal means OR, not AND: Accessible to any code in the same assembly, OR to derived classes in other assemblies. It is the union of protected and internal, so it is more permissive than either alone.

  • private protected is the AND (C# 7.2): Accessible only to derived classes that are also in the same assembly: the intersection, the most restrictive of the combined forms.

Q8.
Explain the difference between method overriding and method overloading.

Junior

Overriding redefines an inherited virtual method in a derived class (runtime polymorphism, same signature), while overloading provides multiple methods with the same name but different parameter lists in the same scope (compile-time resolution). They sound similar but operate at different times and for different reasons.

  • Overriding:

    • Requires virtual/abstract in the base and override in the derived class; signature must match exactly.

    • Resolved at runtime via the virtual method table, so the call dispatches to the actual object's type (dynamic dispatch).

  • Overloading:

    • Same name, different parameters (count, types, or order); return type alone does not count.

    • Resolved at compile time by overload resolution based on the argument types provided.

  • Common confusion: new hides (shadows) a base member rather than overriding it; the call then depends on the declared type, not the runtime type.

csharp

class Animal { public virtual string Speak() => "..."; } class Dog : Animal { public override string Speak() => "Woof"; } // override class Printer { void Print(int x) { } // overload void Print(string s) { } // overload }

Q9.
What are the different types of classes in C# (abstract, static, partial, sealed)?

Junior

These keywords describe how a class can be used or extended rather than separate kinds of objects. A class can be abstract, static, partial, or sealed (and some combine), each constraining instantiation, inheritance, or how the source is organized.

  • abstract: Cannot be instantiated; meant to be a base. May contain abstract members that derived classes must implement.

  • static: Cannot be instantiated or inherited; holds only static members. Used for stateless utility/helper containers (e.g. Math).

  • sealed: Cannot be used as a base class. Used to lock down behavior and can enable minor performance optimizations (devirtualization).

  • partial: Splits one class definition across multiple files, combined at compile time. Common with generated code (designers, source generators) so hand-written and generated members coexist.

  • Note: abstract and sealed are mutually exclusive on a class, since one demands inheritance and the other forbids it.

Q10.
What is the difference between the is and as operators?

Junior

Both test/convert types at runtime, but is returns a bool (does this object match this type?) while as attempts a reference/nullable conversion and returns null on failure instead of throwing.

  • is is a type test: Returns true/false; with pattern matching it can also bind a variable: if (obj is string s).

  • as is a safe cast:

    • Works only on reference types and nullable value types; yields null if the conversion fails (no exception).

    • Cannot do user-defined conversions or unbox to a non-nullable value type.

  • Contrast with a direct cast (T)obj: Throws InvalidCastException on failure, so use it when a wrong type is a bug, not an expected case.

  • Performance tip: obj is string s does the test and cast once; avoid is followed by a separate cast (double check).

Q11.
What is the difference between String and StringBuilder in terms of memory allocation?

Junior

string is immutable, so every modification allocates a new object on the heap, while StringBuilder maintains a mutable internal buffer that can be appended to in place, drastically reducing allocations for repeated changes.

  • string: new object per change: Concatenating in a loop creates many intermediate strings, all garbage; this is O(n²) work in the size of the result.

  • StringBuilder: reusable growable buffer:

    • Appends write into the existing buffer; when capacity is exceeded it reallocates (typically doubling), so amortized cost is much lower.

    • Call ToString() once at the end to materialize the final string.

  • When to use which:

    • Use StringBuilder for many concatenations in a loop or unknown count.

    • For a few known concatenations, plain + or interpolation is fine (the compiler often optimizes these to String.Concat).

Q12.
How are enums represented in C#, and what is the [Flags] attribute used for?

Junior

An enum is a value type backed by an integral type (int by default) that gives named constants; the [Flags] attribute marks an enum whose values are meant to be combined as bitwise flags.

  • Representation:

    • Each member is a named constant of the underlying integral type; defaults start at 0 and increment by 1.

    • You can change the base type, e.g. enum Status : byte, and assign explicit values.

    • Enums are value types stored as their numeric value; casting to/from the underlying type is explicit.

  • [Flags] purpose:

    • Signals the enum is a bit field so members can be OR-combined into one value.

    • Assign powers of two (1, 2, 4, 8) so each bit is independent.

    • It mainly improves ToString() (prints comma-separated names) and signals intent; it does not by itself enforce bitwise rules.

    • Test membership with HasFlag() or a bitwise & check.

csharp

[Flags] enum Access { None = 0, Read = 1, Write = 2, Execute = 4 } var a = Access.Read | Access.Write; // "Read, Write" bool canWrite = a.HasFlag(Access.Write); // true

Q13.
What is the difference between First, FirstOrDefault, Single, and SingleOrDefault in LINQ?

Junior

They differ on two axes: how many elements they expect (one vs at most one) and what happens when none match. First/Single throw on an empty result; the OrDefault variants return default(T) instead.

  • First:

    • Returns the first match; throws InvalidOperationException if the sequence is empty.

    • Stops at the first hit, so it does not scan the rest.

  • FirstOrDefault: Same as First but returns null/zero/default when nothing matches.

  • Single:

    • Asserts exactly one match; throws if there are zero or more than one.

    • Must scan past the first element to verify uniqueness.

  • SingleOrDefault: Returns the one match, the default if none, but still throws if more than one matches.

  • Choosing between them: Use Single when uniqueness is an invariant you want enforced (e.g. lookup by primary key); use First when you just want the top item.

Q14.
Explain the null-coalescing (??) and null-conditional (?.) operators and when you'd use them.

Junior

Both reduce null-handling boilerplate: ?? supplies a fallback value when something is null, and ?. safely accesses a member only when the target isn't null (otherwise yielding null).

  • Null-coalescing ??:

    • a ?? b returns a if non-null, else b. Great for defaults.

    • The compound form x ??= y assigns y only if x is currently null.

  • Null-conditional ?.:

    • a?.b returns null instead of throwing when a is null; a?[i] does the same for indexers.

    • It short-circuits: in a?.b.c, if a is null the whole expression is null and .c is never evaluated.

  • Combining them is the common idiom: customer?.Name ?? "Unknown" safely reads then defaults.

  • Note on value types: ?. on a value-returning member produces a nullable: list?.Count is int?, not int.

csharp

string name = customer?.Name ?? "Unknown"; int? count = items?.Count; // null if items is null cache ??= new Dictionary<string,int>(); // assign only if null

Q15.
What are the benefits of generics, and how do they differ from using object?

Junior

Generics let you write type-safe, reusable code parameterized by type, giving compile-time checking and no boxing, whereas object throws away type information and forces casts and (for value types) boxing.

  • Type safety: A List<int> can only hold int; errors are caught at compile time. With object a wrong type slips in and fails at runtime on the cast.

  • Performance: Storing value types as object boxes them (heap allocation + GC pressure) and unboxes on read. Generics avoid this entirely.

  • No casting: You read the actual type directly, so code is cleaner and there's no risk of an InvalidCastException.

  • Reusability: One generic definition serves all types, instead of writing many type-specific versions or losing safety with object.

  • How the runtime handles it: The CLR specializes generics: reference types share one implementation, while each value type gets its own JIT-compiled, non-boxing version.

Q16.
What is the difference between Func, Action, and Predicate delegates?

Junior

They are all built-in generic delegate types for passing methods around; the difference is whether they return a value and what that return type is.

  • Func<...> returns a value:

    • The last type parameter is the return type; the rest are inputs. Func<int,int,string> takes two ints and returns a string.

    • Supports up to 16 input parameters.

  • Action<...> returns void: For methods that do something but return nothing; Action<string> takes a string and returns nothing. Plain Action takes no args.

  • Predicate<T> returns bool: Takes one T and returns bool; semantically a test. It's equivalent to Func<T,bool>, and many APIs (like LINQ) use the Func form instead.

  • Takeaway: Choose by return: value (Func), nothing (Action), or a boolean test (Predicate).

Q17.
Explain how try/catch/finally works and what exception filters (when clauses) add.

Junior

try runs guarded code, catch handles a matching exception, and finally always runs for cleanup whether or not an exception occurred. Exception filters (when) add a condition that decides whether a catch even applies, without unwinding the stack.

  • try / catch: When code in try throws, the runtime finds the first catch whose type matches and runs it; if none matches, the exception propagates up.

  • finally:

    • Always executes (normal exit, handled exception, or rethrow), used to release resources like files or locks.

    • A using block is sugar for a try/finally that calls Dispose().

  • Exception filters (when):

    • catch (Ex e) when (condition) only enters the block if condition is true; otherwise the search continues to other handlers.

    • Key benefit: if the filter is false the stack is NOT unwound, so the original throw point is preserved for debugging (better than catching and rethrowing).

    • Useful for conditional handling (e.g. retry only on transient errors) or logging side effects via when (Log(e)).

csharp

try { Call(); } catch (HttpRequestException e) when (e.StatusCode == 503) { Retry(); // only for 503; other statuses skip this catch } finally { Cleanup(); // always runs }

Q18.
Explain the 'using' statement. What happens to the object when the block execution finishes?

Junior

The using statement guarantees deterministic cleanup of an IDisposable object: when the block ends, Dispose() is called automatically, even if an exception is thrown.

  • What happens at block end:

    • The compiler wraps the block in a try/finally and calls Dispose() in the finally.

    • This releases unmanaged resources (file handles, connections) promptly rather than waiting for the GC.

  • Exception safety: Because cleanup is in finally, the resource is freed even if the body throws.

  • using declaration (C# 8+): Writing using var x = ...; disposes x at the end of the enclosing scope, avoiding nesting.

  • The object is not garbage collected at block end: only Dispose() runs; memory is reclaimed later by the GC.

csharp

using (var conn = new SqlConnection(cs)) { conn.Open(); // ... use conn } // conn.Dispose() called here, even on exception

Q19.
Explain the difference between a struct and a class. When would you choose one over the other?

Mid

A struct is a value type stored inline and copied by value, while a class is a reference type stored on the heap and copied by reference. Choose a struct for small, immutable, short-lived data; choose a class for most everything else.

  • Memory and copy semantics:

    • struct: value type, copied on assignment/passing, often lives on the stack or inline in its container.

    • class: reference type, the variable holds a reference to a heap object; copies share the same instance.

  • Inheritance and defaults:

    • Structs can't inherit from another struct/class (but can implement interfaces); classes support full inheritance.

    • A struct's fields default to zero/null and it always has an implicit parameterless constructor; a class reference defaults to null.

  • Equality: Structs compare by value by default; classes compare by reference unless overridden.

  • When to choose a struct: Small (roughly <= 16 bytes), immutable, logically a single value (e.g. Point, DateTime), and used in large quantities to reduce GC pressure.

  • When to choose a class: Identity matters, the object is large or mutable, or you need inheritance/polymorphism. This is the default choice.

Q20.
What is boxing and unboxing, and why is it generally considered a performance bottleneck?

Mid

Boxing wraps a value type in a heap-allocated object so it can be treated as object (or an interface); unboxing extracts the value back out. It's costly because each box is a heap allocation plus a copy, adding GC pressure.

  • Boxing: Allocates a new object on the heap and copies the value into it, e.g. assigning an int to an object.

  • Unboxing: Casts the boxed reference back to the value type, copying the value out; requires an exact type match or throws InvalidCastException.

  • Why it's a bottleneck:

    • Each box is a heap allocation, so tight loops create lots of garbage and trigger GC.

    • Extra indirection and copying hurt cache locality and throughput.

  • How to avoid it: Use generics (List<int>, not ArrayList) so values stay typed; implement generic interfaces; prefer Span<T> and value-type-aware APIs.

csharp

int n = 42; object boxed = n; // boxing: heap allocation + copy int back = (int)boxed; // unboxing: copy out, exact type required

Q21.
Can a struct implement an interface? Can it inherit from a class?

Mid

Yes, a struct can implement one or more interfaces, but no, it cannot inherit from a class (or another struct): structs implicitly derive from System.ValueType and support no further inheritance.

  • Implementing interfaces:

    • A struct can satisfy interface contracts like IComparable or IEquatable<T>.

    • Watch out: calling an interface method on a struct stored as the interface type boxes it, copying the value to the heap.

  • No class inheritance:

    • Structs are implicitly sealed and inherit only from ValueType; they can't serve as a base or derive from a base, so no virtual/override class hierarchy.

    • They can override the methods inherited from object (Equals, GetHashCode, ToString).

csharp

public struct Money : IComparable<Money> // interface: OK { public decimal Amount; public int CompareTo(Money other) => Amount.CompareTo(other.Amount); } // public struct Money : SomeClass // NOT allowed

Q22.
What is an 'Assembly' in .NET, and what is the difference between a Private and a Shared Assembly?

Mid

An assembly is the fundamental unit of deployment, versioning, and security in .NET: a compiled .dll or .exe containing IL, metadata, and a manifest. A private assembly is used by a single application, while a shared assembly is installed centrally for use by many applications.

  • What an assembly contains:

    • IL code, type metadata, resources, and a manifest describing identity, version, and dependencies.

    • It's the boundary for type identity and versioning (the CLR's smallest deployable, versioned unit).

  • Private assembly:

    • Deployed in the application's own folder and used only by that application.

    • Needs no strong name; simplest and most common deployment model.

  • Shared assembly:

    • Intended to be shared by multiple applications; in .NET Framework it requires a strong name and is registered in the GAC (Global Assembly Cache).

    • Supports side-by-side versioning so different apps can bind to different versions.

    • Note: modern .NET (Core and later) dropped the GAC and favors private, self-contained deployment.

Q23.
Explain the role of the Just-In-Time (JIT) compiler in the .NET execution process.

Mid

The JIT compiler converts the platform-independent IL in an assembly into native machine code at runtime, just before a method first executes, so managed code can run on the actual hardware.

  • Two-stage compilation:

    • C# is first compiled to IL + metadata; the JIT then produces native code on the target machine.

    • This is why one assembly can run on different CPU architectures.

  • Compiles on demand and caches:

    • Each method is JIT-compiled the first time it's called; the native result is reused for later calls.

    • It can apply runtime optimizations using actual CPU and type information.

  • Alternatives and tiers:

    • Tiered compilation starts fast then re-JITs hot methods with more optimization.

    • AOT options (ReadyToRun, Native AOT) precompile to native code to cut startup/JIT cost.

Q24.
What are Attributes in C#, and how can they be retrieved at runtime?

Mid

Attributes are declarative metadata you attach to code elements (classes, methods, properties); they don't change behavior by themselves but can be read at runtime via reflection to drive logic.

  • What they are:

    • Classes deriving from System.Attribute, applied with square-bracket syntax like [Obsolete] or [Serializable].

    • Stored as metadata in the assembly.

  • Common uses: Validation, serialization control, ORM mapping, and framework hooks (e.g. ASP.NET routing).

  • Defining your own: Subclass Attribute and optionally restrict targets with [AttributeUsage].

  • Retrieving at runtime: Use reflection: GetCustomAttributes() or GetCustomAttribute<T>() on a MemberInfo/Type.

csharp

[AttributeUsage(AttributeTargets.Class)] class AuthorAttribute : Attribute { public string Name { get; } public AuthorAttribute(string name) => Name = name; } [Author("Ada")] class Report { } // Read it back via reflection var attr = typeof(Report).GetCustomAttribute<AuthorAttribute>(); Console.WriteLine(attr?.Name); // "Ada"

Q25.
Explain the difference between an interface and an abstract class. When is one preferred over the other?

Mid

An interface is a pure contract of members a type must implement, while an abstract class is a partial base that can mix abstract members with shared implementation and state; choose an interface for capability across unrelated types and an abstract class for a common base with shared code.

  • Interface:

    • Defines members with no instance state; a type can implement many interfaces (multiple inheritance of type).

    • Best for describing a capability (IDisposable, IComparable) that unrelated classes can share.

  • Abstract class:

    • Can have fields, constructors, and concrete methods alongside abstract ones; a class inherits only one.

    • Best when you want a shared base implementation plus a common identity ("is-a" relationship).

  • Choosing between them:

    • Need shared code/state or a base type: abstract class. Need a contract across diverse types or multiple inheritance: interface.

    • Nuance: modern C# allows default interface methods, narrowing the gap, but interfaces still hold no instance state.

Q26.
What is the purpose of the sealed keyword? Why might you seal a class for performance reasons?

Mid

The sealed keyword prevents a class from being inherited (or a virtual member from being further overridden), expressing intent and enabling the runtime to optimize calls.

  • What it does:

    • On a class: blocks any subclassing.

    • On an overridden method: stops further overriding down the hierarchy (sealed override).

  • Why seal: design intent: Guarantees behavior can't be altered by inheritance, protecting invariants and security.

  • Why seal: performance:

    • With no possible derived type, the JIT can devirtualize calls (resolve them statically) and even inline them, skipping virtual dispatch.

    • Type checks like is and casts can also be cheaper because the exact type is known.

  • Practical note: The gains are usually small; seal primarily for correctness/intent, treating performance as a bonus.

Q27.
What are extension methods, and how do they achieve polymorphism without inheritance?

Mid

Extension methods are static methods that let you call new methods on an existing type as if they were instance members, without modifying or inheriting from that type. They achieve a form of polymorphism by adding behavior to types you don't own (including sealed types and interfaces) through composition rather than a class hierarchy.

  • Definition:

    • A static method in a static class whose first parameter is prefixed with this, naming the type being extended.

    • The compiler rewrites value.MyMethod() into a normal static call MyClass.MyMethod(value).

  • Polymorphism via interfaces: Extending an interface (like IEnumerable<T>) gives every implementer the behavior, so one method applies polymorphically across many concrete types (this is how LINQ works).

  • Not true (virtual) polymorphism:

    • Resolution is static and compile-time based on the declared type, not the runtime type, so there is no virtual dispatch or overriding.

    • An instance method with the same signature always wins over an extension method.

  • Use cases: adding helpers to sealed/BCL types, fluent APIs, and keeping interfaces small while sharing logic.

csharp

public static class StringExtensions { public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); } // Called as if it were an instance method: bool empty = name.IsNullOrEmpty();

Q28.
What is a static constructor, and when is it executed?

Mid

A static constructor initializes a type's static data and runs exactly once per type, automatically, before the type is first used. You cannot call it directly or give it access modifiers or parameters; the runtime invokes it.

  • When it runs:

    • Lazily, at the latest just before the first instance is created or any static member is accessed.

    • Guaranteed to run only once, and the runtime ensures thread-safe execution.

  • Rules:

    • Declared without access modifiers and without parameters, using the static keyword.

    • A type can have only one static constructor.

  • Gotchas:

    • Presence of a static constructor can suppress the beforefieldinit optimization, slightly delaying initialization timing.

    • An exception thrown in it makes the type unusable for the rest of the app domain's life (TypeInitializationException).

csharp

class Config { public static readonly string Path; static Config() // no modifiers, no params { Path = LoadPath(); // runs once, before first use } }

Q29.
Explain the difference between ref, out, and in parameters. Why would you use in for performance?

Mid

All three pass arguments by reference, but they differ in direction and intent: ref is read-write (must be initialized by the caller), out is write-only output (must be assigned by the callee), and in is read-only input passed by reference for performance. They are part of the method signature and require the keyword at both declaration and call site.

  • ref: Two-way: the variable must be initialized before the call, and the method may read and modify it.

  • out: Output-only: need not be initialized before the call, but the method must assign it before returning. Useful for returning multiple values (e.g. int.TryParse).

  • in: Read-only reference: the method receives a reference but cannot modify the argument.

  • Why in helps performance:

    • For large value types (big structs), it avoids copying the whole struct onto the stack by passing a reference, while the readonly guarantee preserves value semantics.

    • Caveat: if the type isn't readonly, the compiler may make defensive copies on member access, which can negate the gain.

csharp

if (int.TryParse("42", out int value)) // out: callee assigns Console.WriteLine(value); void Swap(ref int a, ref int b) { (a, b) = (b, a); } // ref: read-write double Distance(in Vector3 v) => v.Length(); // in: readonly, no copy

Q30.
Explain the difference between var, dynamic, and object.

Mid

All three can hold "any" value, but they differ in when the type is resolved: var is compile-time static typing with inference, object is the static base type with runtime polymorphism, and dynamic defers all member resolution to runtime.

  • var: compile-time inferred, fully static: The compiler picks the exact type from the initializer; var x = 5 is literally int. No runtime cost, full IntelliSense and type checking.

  • object: the universal base type: You can assign anything, but you only see object members; using the real type needs a cast. Value types get boxed.

  • dynamic: resolution skipped until runtime: Compiler accepts any member access; errors surface at runtime as RuntimeBinderException. Built on the DLR, useful for interop (COM, JSON, IronPython).

  • Rule of thumb: prefer var for clean static code, object when you truly need a common base, dynamic only for dynamic/interop scenarios.

Q31.
What is the difference between == and the Equals() method in C#?

Mid

== is an operator resolved at compile time, while Equals() is a virtual method resolved at runtime; they can behave differently because == can be overloaded and defaults to reference comparison for classes.

  • == (operator, static dispatch):

    • For value types it compares values; for reference types it compares references unless the type overloads == (e.g. string overloads it for value semantics).

    • Bound by the compile-time type, so it ignores overrides on a more derived runtime type.

  • Equals() (virtual, dynamic dispatch): Calls the most-derived override based on the runtime type; the default object.Equals does reference equality unless overridden.

  • Common gotcha: Comparing two boxed ints via == as object gives reference comparison, but Equals() gives value comparison.

  • If you override Equals(), also override GetHashCode(), and ideally overload == to stay consistent.

Q32.
What is operator overloading, and what are the rules and limitations around it?

Mid

Operator overloading lets a type define how operators like + or == behave for its instances, via public static methods using the operator keyword.

  • How you declare it: Must be public static, with at least one parameter of the containing type.

  • Pairing rules:

    • Some operators must come in pairs: overload == and you must also overload !=; same for </> and <=/>=.

    • Overloading == should be matched by overriding Equals() and GetHashCode().

  • Limitations:

    • Cannot create new operators or change precedence/arity.

    • Some operators can't be overloaded directly: =, &&, ||, ?:, . (compound assignment like += comes for free from +).

  • Use sparingly: only when the operation is intuitive (math types, money, vectors), or it hurts readability.

csharp

public readonly struct Money { public decimal Amount { get; } public Money(decimal a) => Amount = a; public static Money operator +(Money a, Money b) => new Money(a.Amount + b.Amount); }

Q33.
How do implicit and explicit conversion operators work in C#?

Mid

They are user-defined conversions declared with static methods using implicit or explicit: implicit conversions happen automatically, while explicit ones require a cast.

  • implicit conversions: Applied automatically by the compiler; use only when the conversion is always safe and lossless (no data loss, no exceptions).

  • explicit conversions: Require an explicit cast (T)x; use when the conversion can lose information or fail, signaling intent to the caller.

  • Declaration form: Written as public static implicit operator TargetType(SourceType x); must be on the source or target type.

  • Caveat: Overusing implicit conversions can hide bugs and surprising behavior; prefer explicit (or a named method) when in doubt.

csharp

public readonly struct Celsius { public double Degrees { get; } public Celsius(double d) => Degrees = d; // safe, automatic public static implicit operator double(Celsius c) => c.Degrees; // narrowing, needs a cast public static explicit operator Celsius(double d) => new Celsius(d); }

Q34.
Why are strings immutable in C#, and what is string interning?

Mid

Strings are immutable so that they can be safely shared, cached, and used as keys without defensive copying; interning is the runtime's pool of unique string instances so identical literals share one object in memory.

  • Why immutability:

    • Thread safety: shared strings need no locking since no one can change them.

    • Hash stability: safe as dictionary keys because the hash code never changes.

    • Security and sharing: callers can hold a reference without fear it mutates underneath them.

  • String interning:

    • Identical compile-time literals point to a single pooled instance, saving memory.

    • You can intern runtime strings with String.Intern() and check with String.IsInterned().

    • Consequence: two equal literals are reference-equal, but two strings built at runtime usually are not.

  • Caveat: don't intern large/transient strings, since interned strings live for the app's lifetime.

Q35.
What is the difference between a Tuple and a ValueTuple?

Mid

Both group multiple values, but Tuple (the old System.Tuple) is a reference type with read-only Item1/Item2 properties, while ValueTuple (the (int, string) syntax) is a mutable value type with named, optionally-elided fields.

  • Tuple (class, since .NET 4.0):

    • Reference type: heap-allocated, adds GC pressure.

    • Immutable fields exposed as Item1, Item2; no custom names.

  • ValueTuple (struct, since C# 7):

    • Value type: usually stack-allocated, no heap allocation.

    • Mutable public fields, and supports element names and deconstruction.

  • Practical impact: Prefer ValueTuple for lightweight method returns; it's the modern default with friendlier syntax.

csharp

// ValueTuple with named elements + deconstruction (string Name, int Age) GetPerson() => ("Ada", 36); var (name, age) = GetPerson(); // name == "Ada", age == 36

Q36.
Explain the concept of Deferred Execution in LINQ. Why is it important for performance?

Mid

Deferred execution means a LINQ query is not run when it is defined, but only when it is enumerated (e.g. in a foreach, or by ToList()/Count()). The query object just describes the work; iterating it pulls the data.

  • How it works:

    • Operators like Where and Select return an iterator that captures the query, not the results.

    • Execution happens lazily and element-by-element when enumerated.

  • Why it matters for performance:

    • Composition: chained operators run in a single pass instead of building intermediate collections.

    • Short-circuiting: First() or Take(5) stops early without processing the whole source.

    • With IQueryable, the full expression tree is translated to one SQL query rather than fetching everything.

  • Pitfalls:

    • Multiple enumeration re-runs the query each time (re-querying the DB or re-reading a stream).

    • Captured variables and a changing source can give different results later; materialize with ToList() to snapshot.

Q37.
What is the difference between IEnumerable<T> and IQueryable<T>? When would you choose one over the other?

Mid

Both represent a query, but IEnumerable<T> executes in memory using delegates (LINQ to Objects), while IQueryable<T> builds an expression tree that a provider (e.g. EF Core) translates to another language like SQL and runs at the source.

  • IEnumerable<T>:

    • Operates on in-memory objects; query logic is compiled .NET code.

    • If used against a DB result, it pulls data into memory first, then filters client-side.

  • IQueryable<T>:

    • Holds an Expression tree so the provider can translate and execute remotely.

    • Filtering, paging, and projection run at the database, reducing data transferred.

  • When to choose:

    • Use IQueryable for remote data sources you want to query efficiently (databases).

    • Use IEnumerable for in-memory collections, or after data is already loaded.

    • Trap: casting an IQueryable to IEnumerable too early forces the rest of the query to run in memory.

Q38.
When would you use a yield return statement?

Mid

Use yield return when you want to produce a sequence lazily, returning elements one at a time without first building and storing the whole collection.

  • Good use cases:

    • Streaming large or infinite sequences where materializing everything is wasteful or impossible.

    • Custom filtering/transforming logic that composes with LINQ and supports deferred execution.

    • Avoiding an explicit temporary List<T> just to return results.

  • Behavior:

    • Each yield return hands back a value and pauses, resuming where it left off on the next MoveNext().

    • The method body does not run until the result is enumerated (deferred).

    • yield break ends the sequence early.

csharp

IEnumerable<int> Evens(int max) { for (int i = 0; i <= max; i += 2) yield return i; // produced lazily, one at a time }

Q39.
What is the difference between Array.Sort() and LINQ's .OrderBy()?

Mid

Array.Sort() sorts an array in place (mutating it) and returns nothing, while LINQ's .OrderBy() leaves the source untouched and returns a new, lazily-evaluated ordered sequence.

  • Array.Sort():

    • In-place mutation: the original array is reordered.

    • Uses an unstable introsort, so equal elements may change relative order.

    • Works only on arrays (and similar) and runs immediately.

  • .OrderBy():

    • Non-mutating: returns a new ordered IEnumerable<T>, source unchanged.

    • Stable sort: equal elements keep their original order.

    • Deferred: sorting happens when enumerated, and composes with ThenBy, Where, etc.

  • Choosing:

    • Prefer Array.Sort() for max performance on an array you own and can mutate.

    • Prefer .OrderBy() for readability, stability, immutability, and chained queries.

Q40.
What is the difference between IEnumerable<T>, ICollection<T>, and IQueryable<T>?

Mid

They form a capability ladder: IEnumerable<T> only lets you iterate, ICollection<T> adds count and mutation (add/remove), and IQueryable<T> extends enumerable with remote, expression-tree-based querying.

  • IEnumerable<T>:

    • The minimal contract: forward, read-only iteration via GetEnumerator().

    • No Count, no indexing, no modification.

  • ICollection<T>:

    • Inherits IEnumerable<T> and adds Count, Add, Remove, Contains.

    • Represents a finite, modifiable in-memory collection (implemented by List<T>, etc.).

  • IQueryable<T>:

    • Inherits IEnumerable<T> and carries an Expression and a provider.

    • Enables translating queries to a remote source (e.g. SQL) for server-side execution.

  • Rule of thumb: Expose IEnumerable<T> for read-only iteration, ICollection<T> when callers need to modify, IQueryable<T> for composable remote queries.

Q41.
What is the difference between Select and SelectMany?

Mid

Select projects each element to one result, giving a sequence of sequences when the projection itself returns a collection, whereas SelectMany projects each element to a collection and then flattens them all into one sequence.

  • Select:

    • One-to-one mapping: N inputs produce N outputs.

    • Projecting to a list yields IEnumerable<List<T>> (nested).

  • SelectMany:

    • One-to-many then flatten: combines inner collections into a single flat sequence.

    • Ideal for parent/child relationships (e.g. all orders across all customers).

    • Has an overload to also project the parent and child together.

csharp

var customers = ...; // each has List<Order> Orders // Select: IEnumerable<List<Order>> (nested) var nested = customers.Select(c => c.Orders); // SelectMany: IEnumerable<Order> (flat) var allOrders = customers.SelectMany(c => c.Orders);

Q42.
Explain the yield keyword and how it creates an iterator state machine.

Mid

The yield keyword lets a method produce a sequence incrementally; the C# compiler rewrites such a method into a hidden class implementing IEnumerator<T> that is a state machine, so execution pauses and resumes between elements.

  • What yield does:

    • yield return x emits a value and suspends the method, preserving locals and position.

    • yield break terminates the sequence.

  • The generated state machine:

    • The compiler creates a class with a state field, current field, and lifted local variables.

    • Each MoveNext() call uses state to jump to where the method last yielded, runs to the next yield, sets Current, and returns true (or false when done).

  • Consequences:

    • Execution is deferred: nothing runs until the iterator is enumerated.

    • It is lazy and stateful per enumerator, so each foreach gets a fresh walk.

    • Cannot use yield inside try/catch with a catch, or in anonymous methods/unsafe blocks.

Q43.
What is the difference between yield return and returning a concrete List<T>?

Mid

A yield return method builds a lazy, streaming iterator that produces items on demand, while returning a concrete List<T> materializes every element up front into memory.

  • yield return is deferred and lazy:

    • The compiler generates a state machine implementing IEnumerable<T>; nothing runs until you enumerate.

    • Items are produced one at a time, so memory stays low even for huge or infinite sequences.

    • Re-enumerating runs the method again from the start.

  • A concrete List<T> is eager:

    • All work happens before the method returns; the full collection sits in memory.

    • You can index, get .Count, and iterate repeatedly without recomputation.

  • Trade-offs to mention:

    • Lazy iterators compose well in LINQ pipelines and short-circuit (e.g. Take) without computing everything.

    • Deferred execution can surprise: exceptions and side effects fire at enumeration time, not call time.

Q44.
What is a delegate, and how does it differ from an event?

Mid

A delegate is a type-safe reference to a method (essentially a typed function pointer that can be invoked or passed around). An event is a restricted wrapper built on top of a delegate that exposes only subscribe/unsubscribe to the outside world.

  • Delegate basics:

    • Declared with delegate or used via built-ins like Action, Func, Predicate.

    • Multicast: you can combine handlers with += and invoke them all.

    • Anyone holding the delegate can invoke it or reassign it with =.

  • An event adds encapsulation:

    • The event keyword limits outside code to += and -= only.

    • Subscribers cannot raise (invoke) the event or clear other subscribers; only the declaring class can fire it.

  • Why it matters: Events implement the publish/subscribe pattern safely; a raw public delegate field would let callers overwrite or invoke it, breaking that contract.

csharp

public class Button { public event Action? Clicked; // subscribers can only += / -= protected void OnClick() => Clicked?.Invoke(); // only this class raises it }

Q45.
What are the different dependency injection lifetimes in .NET?

Mid

The built-in container in Microsoft.Extensions.DependencyInjection supports three lifetimes that control how often a service instance is created and shared: singleton, scoped, and transient.

  • Singleton (AddSingleton):

    • One instance for the entire application lifetime, shared by all requests.

    • Must be thread-safe; good for stateless services, caches, configuration.

  • Scoped (AddScoped):

    • One instance per scope; in ASP.NET Core a scope is one HTTP request.

    • Typical for per-request state like an EF Core DbContext.

  • Transient (AddTransient):

    • A new instance every time it is requested.

    • Best for lightweight, stateless services.

  • The captive dependency trap: Never inject a scoped (or transient) service into a singleton: the singleton captures it and effectively makes it live forever, causing stale state or threading bugs.

Q46.
Explain the concept of Inversion of Control (IoC) and how it is supported natively in the .NET Base Class Library.

Mid

Inversion of Control is the principle of handing control over object creation and flow to an external mechanism instead of hardcoding it inside your class. .NET supports it natively through the dependency injection container in Microsoft.Extensions.DependencyInjection, which builds and supplies dependencies for you.

  • What IoC means:

    • Traditionally a class news-up its own dependencies; with IoC, something else decides and provides them.

    • Dependency Injection is the most common form of IoC: dependencies are passed in (usually via the constructor).

  • Native .NET support:

    • You register abstractions to implementations in IServiceCollection, then resolve via IServiceProvider.

    • The Generic Host (IHostBuilder) and ASP.NET Core wire this in by default and inject into constructors automatically.

  • Why it helps:

    • Loose coupling and testability: depend on interfaces, swap real implementations for mocks.

    • Centralizes lifetime and wiring decisions in one place.

csharp

// Registration services.AddScoped<IEmailSender, SmtpEmailSender>(); // Consumption: dependency is injected, not created here public class OrderService(IEmailSender sender) { public void Confirm() => sender.Send("Order confirmed"); }

Q47.
What are Nullable Reference Types, and what problem do they solve?

Mid

Nullable Reference Types (NRT) is a C# 8 compile-time feature that lets you express intent about whether a reference can be null, so the compiler can warn you about possible null dereferences before they become runtime NullReferenceExceptions.

  • The problem they solve: Historically any reference could be null, so NullReferenceException (the "billion dollar mistake") was the most common runtime failure.

  • How it works:

    • When enabled, string is non-nullable (compiler warns if it could be null) and string? is nullable (you must null-check before use).

    • It is purely static analysis: a feature of the compiler, not the runtime. The IL is unchanged.

  • Enabling it: Turn on with <Nullable>enable</Nullable> in the project, or per-file with #nullable enable.

  • Escape hatch: The null-forgiving operator ! tells the compiler "trust me, this isn't null", but it suppresses the warning without changing runtime behavior, so use it sparingly.

Q48.
What is Nullable<T> (a nullable value type), and how does it work under the hood?

Mid

Nullable<T> lets a value type (which normally can't be null) represent "no value". int? is just shorthand for Nullable<int>.

  • What it is under the hood:

    • A generic struct holding two fields: a bool HasValue and a T value. It is itself a value type, so it lives on the stack/inline, not the heap.

    • Constrained to where T : struct, so it only wraps value types.

  • Accessing the value:

    • HasValue tells you if it's set; Value returns it but throws InvalidOperationException if null.

    • GetValueOrDefault() returns the value or the type's default safely.

  • Compiler magic:

    • The literal null assigned to int? is compiled into new Nullable<int>() (HasValue false).

    • Boxing is special-cased: a null Nullable<T> boxes to a real null reference, and a non-null one boxes to the underlying T.

  • Operators lift automatically: Arithmetic/comparison "lift" over null: any operand being null usually yields null (or false for comparisons).

Q49.
What are generic constraints (the where clause), and why would you use them?

Mid

Generic constraints (the where clause) restrict what types a generic parameter can be, which both guarantees the type supports what you do with it and unlocks those operations inside the generic code.

  • Why use them: Without a constraint, T is essentially object; you can't call its members. Constraints let the compiler know what T can do.

  • Common constraints:

    • where T : class (reference type) and where T : struct (non-nullable value type).

    • where T : SomeBase or where T : IInterface: T must derive from / implement it, so you can call those members.

    • where T : new(): T has a public parameterless constructor, so you can new T().

    • where T : notnull, where T : unmanaged, and where T : U (one parameter derives from another).

  • Rules: Multiple constraints can be combined, but class/struct must come first and new() must come last.

csharp

T Create<T>() where T : IComparable<T>, new() { var item = new T(); // allowed by new() return item; // CompareTo available via IComparable<T> }

Q50.
What is the difference between IEnumerable, ICollection, and IList?

Mid

They form an inheritance chain of increasing capability: IEnumerable<T> only lets you iterate, ICollection<T> adds size and modification, and IList<T> adds positional (index-based) access.

  • IEnumerable<T>:

    • The most basic: exposes GetEnumerator() for foreach iteration only. No Count, no add/remove.

    • Supports lazy/deferred evaluation, which is why LINQ is built on it.

  • ICollection<T>:

    • Extends IEnumerable<T> with Count, Add, Remove, Contains, and IsReadOnly.

    • You know the size and can modify, but there's no concept of ordering by index.

  • IList<T>: Extends ICollection<T> with indexer access (this[int]), Insert, and RemoveAt.

  • Design guidance: Accept the least specific type you need (often IEnumerable<T>) as a parameter to maximize flexibility, and expose the most capable type your callers actually require.

Q51.
How does a List<T> work internally when it reaches its capacity? What is the Big-O complexity of adding an item?

Mid

A List<T> wraps a backing array; when it fills, it allocates a new larger array (typically doubling capacity) and copies elements over. Adding is amortized O(1) but O(n) on the resize step.

  • Backing array with separate Count and Capacity: Count is how many items you have; Capacity is the array's size.

  • Growth on overflow: When Count == Capacity and you Add, it allocates a new array (doubles capacity, from 0 it starts at 4) and copies all elements: that single op is O(n).

  • Complexity of Add:

    • Amortized O(1): most adds just write to the next slot; resizes are rare and their cost averages out across many adds.

    • Worst case for a single add is O(n) due to the copy.

  • Optimization: If you know the final size, pass it to the constructor (new List<T>(capacity)) to avoid repeated reallocations.

Q52.
What are thread-safe collections like ConcurrentDictionary, and when would you use them?

Mid

Thread-safe collections in System.Collections.Concurrent (like ConcurrentDictionary, ConcurrentQueue, ConcurrentBag) let multiple threads read and write safely without you writing explicit locks, using fine-grained locking or lock-free techniques internally.

  • Why not just lock a Dictionary:

    • A normal Dictionary corrupts or throws under concurrent writes; a single global lock works but serializes all access.

    • ConcurrentDictionary uses striped locking (multiple lock regions) so different buckets can be updated in parallel.

  • Atomic compound operations:

    • Methods like GetOrAdd(), AddOrUpdate(), and TryRemove() do check-then-act safely in one call.

    • Caveat: the value factory in GetOrAdd() may run more than once under contention, though only one result is stored.

  • When to use: Shared state genuinely accessed by multiple threads (caches, producer/consumer queues with BlockingCollection).

  • When not to: Single-threaded or read-only data: the synchronization overhead is wasted. Use a plain or immutable collection.

Q53.
Explain the difference between throw and throw ex. Why does it matter for debugging?

Mid

Both rethrow an exception, but throw preserves the original stack trace while throw ex resets it to the current line, destroying the information about where the error actually originated.

  • throw (rethrow): Inside a catch block, bare throw re-raises the caught exception keeping its original stack trace intact.

  • throw ex (rethrow the variable): Treats it as a new throw point: the stack trace is overwritten to start at this line, so you lose the real source.

  • Why it matters for debugging: Logs and crash dumps point to the catch block instead of the failing code, making bugs far harder to trace.

  • Guidance: Use bare throw to rethrow. To add context, wrap it: throw new MyException("...", ex) preserving the original as InnerException.

csharp

try { DoWork(); } catch (Exception ex) { Log(ex); throw; // keeps original stack trace // throw ex; // WRONG: resets stack trace to this line }

Q54.
What is the relationship between Equals() and GetHashCode(), and why must you override both together?

Mid

Equals() and GetHashCode() form a contract: if two objects are equal, they MUST return the same hash code. Hash-based collections rely on this, so overriding one without the other breaks them.

  • The contract:

    • Equal objects (Equals returns true) must have equal hash codes.

    • The reverse isn't required: unequal objects may share a hash code (a collision), which is allowed.

  • How hash collections use them: Dictionary and HashSet first use GetHashCode() to pick the bucket, then Equals() to confirm the match.

  • What breaks if you override only one: Override Equals only: equal objects may produce different hash codes and land in different buckets, so lookups fail to find them.

  • Practical rules:

    • Base the hash on the same fields used in equality, and ideally make those fields immutable so the hash never changes while in a collection.

    • Use HashCode.Combine(...) to implement it, or use a record which generates both correctly.

Q55.
What are best practices for creating and throwing custom exceptions in C#?

Mid

Custom exceptions should be reserved for cases callers can meaningfully distinguish and handle; derive from Exception (not the obsolete ApplicationException), follow naming and constructor conventions, and carry enough context to diagnose the failure.

  • Only create one when it adds value: Prefer built-in types (ArgumentException, InvalidOperationException) when they fit; a custom type is justified when callers need to catch it specifically.

  • Naming and inheritance: End the class name with Exception and derive directly from Exception.

  • Provide the standard constructors: A parameterless one, one taking string message, and one taking (string message, Exception innerException) so wrapping preserves the original cause.

  • Carry diagnostic context: Add custom properties for relevant data (e.g. an ID or status code) rather than encoding it into the message string.

  • Throwing conventions: Throw on contract violations, not for ordinary control flow; use throw; (not throw ex;) when rethrowing to preserve the stack trace.

  • Make it immutable: Set context via constructors and expose read-only properties.

csharp

public class OrderNotFoundException : Exception { public int OrderId { get; } public OrderNotFoundException() { } public OrderNotFoundException(string message) : base(message) { } public OrderNotFoundException(string message, Exception inner) : base(message, inner) { } public OrderNotFoundException(int orderId) : base($"Order {orderId} was not found.") => OrderId = orderId; }

Q56.
What is the difference between IComparable and IComparer, and when would you use each?

Mid

Both define ordering, but IComparable lets a type define its own natural ordering, while IComparer defines ordering externally, in a separate object, so you can sort the same type multiple different ways.

  • IComparable<T>:

    • Implemented by the type itself via CompareTo(T other); expresses the one "default" sort order.

    • Used automatically by List<T>.Sort() and Array.Sort() with no extra argument.

  • IComparer<T>:

    • A separate class implementing Compare(T x, T y); the type being sorted need not know about it.

    • Lets you define many orderings (by name, by date, descending) and pass them where needed.

  • When to use each:

    • Use IComparable for the obvious natural order you own and control.

    • Use IComparer for alternative orderings, or when sorting a type you can't modify.

  • Convention: Both return negative / zero / positive for less-than / equal / greater-than.

csharp

public class Person : IComparable<Person> { public string Name { get; set; } public int Age { get; set; } // natural order: by name public int CompareTo(Person other) => string.Compare(Name, other.Name, StringComparison.Ordinal); } // alternative order: by age public class AgeComparer : IComparer<Person> { public int Compare(Person x, Person y) => x.Age.CompareTo(y.Age); } people.Sort(); // uses IComparable (by name) people.Sort(new AgeComparer()); // uses IComparer (by age)

Q57.
Explain the difference between a Thread, a Task, and a Thread Pool thread.

Mid

These are layers of abstraction: a Thread is a raw OS thread you create and manage, a thread pool thread is a reusable worker the runtime manages for you, and a Task is a higher-level promise of future work that usually runs on the thread pool.

  • Thread:

    • A dedicated OS thread; creation is expensive (around 1 MB stack) and you own its lifetime.

    • Justified for long-running or specialized work needing a fixed thread (e.g. priority or apartment state).

  • Thread pool thread:

    • A pre-created, reusable worker managed by the runtime; avoids per-task creation cost by recycling threads.

    • Best for short, frequent work items; the pool grows and shrinks automatically.

  • Task:

    • An abstraction over a unit of work plus its eventual result/exception; supports composition, continuations, cancellation, and await.

    • By default schedules onto the thread pool, but a Task isn't necessarily a thread: an I/O-bound task uses no thread while it waits.

  • Practical guidance: Prefer Task (and async/await) for almost all concurrency; drop to a raw Thread only for rare specialized needs.

Q58.
What is the difference between Task.Wait() and await Task?

Mid

Both wait for a task to finish, but await is non-blocking and Task.Wait() blocks the calling thread. await yields control back to the caller and resumes later, while Wait() parks the thread until completion.

  • await Task:

    • Suspends the method without blocking the thread; the thread is freed to do other work.

    • Unwraps exceptions directly: a single exception is rethrown as-is.

    • Resumes on the captured context by default (UI thread, etc.) unless ConfigureAwait(false) is used.

  • Task.Wait():

    • Blocks the current thread synchronously until the task completes.

    • Wraps exceptions in an AggregateException, forcing you to dig into InnerException.

    • Can deadlock in contexts with a single-threaded SynchronizationContext (classic UI/ASP.NET deadlock).

  • Rule: use await in async code; reserve Wait() for genuinely synchronous entry points where blocking is acceptable.

Q59.
What is the risk of using .Result or .Wait() on an asynchronous task?

Mid

Using .Result or .Wait() synchronously blocks the calling thread on an async operation, which can cause deadlocks, wastes threads, and obscures exceptions. This is the well-known "sync over async" anti-pattern.

  • Deadlock risk: In a context that captures a single thread (older ASP.NET, WPF/WinForms UI), the blocked thread is the very one the continuation needs to resume on, so both wait forever.

  • Thread waste: A thread sits idle blocked instead of being returned to the pool, hurting scalability under load.

  • Exception wrapping: Errors surface as AggregateException rather than the original exception, making handling clumsier.

  • The fix:

    • Make the method async and await the task all the way up the call chain.

    • If you truly must block, ConfigureAwait(false) throughout the chain mitigates the deadlock but doesn't fix the design.

csharp

// Deadlock-prone var data = GetDataAsync().Result; // blocks; may never resume // Correct var data = await GetDataAsync();

Q60.
How does a CancellationToken work to stop an asynchronous operation?

Mid

A CancellationToken is a cooperative signal: the caller requests cancellation through a CancellationTokenSource, and the running operation periodically checks the token and stops itself. Nothing is forcibly aborted; the operation must cooperate.

  • The source and the token:

    • You create a CancellationTokenSource and pass its .Token into async methods.

    • Calling cts.Cancel() sets the token's IsCancellationRequested to true.

  • How operations respond:

    • They call token.ThrowIfCancellationRequested() or check IsCancellationRequested in loops.

    • Built-in async APIs accept a token and throw OperationCanceledException when triggered.

  • Useful features:

    • CancelAfter(timeout) cancels automatically after a delay.

    • Register(callback) runs a callback on cancellation; CreateLinkedTokenSource combines multiple tokens.

  • Always dispose the CancellationTokenSource and treat OperationCanceledException as expected control flow, not an error.

csharp

using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(5)); await DoWorkAsync(cts.Token); async Task DoWorkAsync(CancellationToken token) { while (true) { token.ThrowIfCancellationRequested(); await Task.Delay(100, token); // also honors the token } }

Q61.
What is the difference between a Finalizer and the IDisposable interface? When would you use one over the other?

Mid

A finalizer is a GC-driven safety net for releasing unmanaged resources non-deterministically, while IDisposable gives the caller deterministic, explicit cleanup via Dispose(). Prefer IDisposable; use a finalizer only as a backup when you directly own unmanaged resources.

  • Finalizer (~ClassName()):

    • Called by the GC at an unpredictable time, on a dedicated finalizer thread.

    • Costly: finalizable objects survive an extra GC cycle (promoted before being collected), hurting performance.

  • IDisposable:

    • Caller decides exactly when to free resources by calling Dispose(), usually via using.

    • Deterministic, so files, sockets, and DB connections are released promptly.

  • Use one over the other:

    • Wrapping managed disposables only: implement IDisposable alone, no finalizer.

    • Holding a raw unmanaged handle: implement both via the Dispose pattern, and call GC.SuppressFinalize(this) in Dispose() so the finalizer is skipped when cleaned up properly.

Q62.
What is a memory leak in a managed environment like C#, and how can it happen?

Mid

In a managed environment a memory leak isn't about forgetting to free memory: the GC reclaims unreachable objects automatically. Instead, a leak happens when objects stay unintentionally reachable from a root, so the GC can never collect them and memory grows over time.

  • Event handlers: Subscribing with += makes the publisher hold a reference to the subscriber; forgetting to unsubscribe keeps it alive (a classic leak).

  • Static references: Anything rooted in a static field (caches, collections) lives for the app's lifetime unless explicitly removed.

  • Undisposed resources: Not calling Dispose() can leak unmanaged memory (OS handles) that the GC doesn't manage.

  • Long-lived captures: Closures, timers, or background tasks that capture objects keep them reachable.

  • Prevention: Unsubscribe events, bound caches, dispose properly, and consider WeakReference for caches that shouldn't prolong lifetime.

Q63.
Explain the IDisposable interface and the using statement. Why is it necessary even with a Garbage Collector?

Mid

IDisposable provides a Dispose() method for deterministic cleanup of resources the GC doesn't manage, and using guarantees it's called even if an exception is thrown. It's necessary because the GC only reclaims managed memory, not unmanaged resources like file handles or sockets.

  • What the GC does and doesn't do:

    • It reclaims managed memory, but on a nondeterministic schedule.

    • It doesn't know how to release unmanaged resources (file handles, DB connections, OS handles) promptly.

  • What IDisposable adds:

    • A contract: call Dispose() to release resources deterministically, right when you're done.

    • Often paired with a finalizer as a safety net, with GC.SuppressFinalize(this) called in Dispose to skip it when cleaned up properly.

  • Why the using statement:

    • It compiles to a try/finally so Dispose() runs even on exceptions.

    • The newer using var declaration disposes at the end of the enclosing scope.

csharp

using (var file = new StreamReader("data.txt")) { return file.ReadToEnd(); } // Dispose() called here, even if ReadToEnd throws

Q64.
What are primary constructors in C# 12, and how do they differ from traditional constructors?

Mid

Primary constructors let you declare constructor parameters directly in the type's header, making those parameters available throughout the class or struct body. In C# 12 they apply to any class or struct (not just records), reducing boilerplate for field assignment and dependency injection.

  • How they work:

    • Parameters in the header are in scope for the whole body: usable in field initializers, methods, and properties.

    • The compiler captures a parameter into hidden state only if it's used outside initialization.

  • Differences from traditional constructors:

    • No explicit this.x = x assignments and no separate constructor body needed for simple cases.

    • For a non-record class they do NOT auto-generate public properties (unlike records); you reference the parameter or assign it to a field yourself.

    • Other constructors must chain to it via : this(...).

csharp

// Great for DI: parameter used directly by methods public class OrderService(IRepository repo, ILogger logger) { public void Place(Order o) { repo.Save(o); logger.Log("saved"); } }

Q65.
What are C# Records, and how do they differ from standard classes?

Mid

Records are reference (or with record struct, value) types designed for immutable data, where the compiler generates value-based equality, a readable ToString(), and nondestructive copying. They differ from standard classes mainly in equality semantics and built-in boilerplate.

  • Value-based equality:

    • Two records are equal if all their fields/properties are equal, whereas classes compare by reference identity by default.

    • The compiler generates Equals, GetHashCode, and == accordingly.

  • Generated members:

    • A useful ToString() that prints property names and values.

    • Positional records get a primary constructor and a Deconstruct method.

  • Nondestructive mutation: The with expression creates a copy with some properties changed, supporting immutability.

  • When to use: Records for DTOs and immutable data models; classes for entities with identity and mutable behavior.

csharp

public record Person(string Name, int Age); var a = new Person("Sam", 30); var b = a with { Age = 31 }; // copy with one change bool same = a == new Person("Sam", 30); // true: value equality

Q66.
What are Collection Expressions in C# 12, and how do they provide a unified syntax for different collection types?

Mid

Collection expressions, introduced in C# 12, give one bracket-based literal syntax [...] for creating arrays, lists, spans, and other collections. The compiler targets the syntax to whatever collection type is expected, so the same form initializes many different types.

  • Unified syntax:

    • The same [1, 2, 3] can build an array, a List<T>, a Span<T>, ImmutableArray<T>, and more, based on the target type.

    • The compiler picks an efficient construction strategy for each target.

  • The spread operator: .. inlines the elements of another collection into the expression, making concatenation concise.

  • Custom type support: Types can opt in via the CollectionBuilder attribute so the literal works for them too.

csharp

int[] arr = [1, 2, 3]; List<int> list = [1, 2, 3]; Span<int> span = [1, 2, 3]; int[] head = [1, 2]; int[] all = [..head, 3, 4]; // spread: [1, 2, 3, 4]

Q67.
Explain pattern matching in C# and when you would use it over a standard if/else or switch block.

Mid

Pattern matching tests a value's shape, type, or structure and optionally extracts data from it in one expression. Use it over plain if/else or a value-based switch when you are branching on type, deconstructing data, or checking combined conditions, because it is more declarative and the compiler checks exhaustiveness.

  • Common pattern kinds:

    • Type pattern: obj is Customer c tests and casts in one step.

    • Relational/logical patterns: is > 0 and < 100, is null or empty.

    • Property and positional patterns: { Status: Active, Age: > 18 } or deconstructed tuples.

    • List patterns (C# 11): [1, .., 9].

  • When to prefer it:

    • Replacing type-check-then-cast chains: cleaner and avoids double casting.

    • switch expressions return a value and force you to handle all cases, reducing bugs.

    • Combining several conditions on one object reads better than nested if.

  • When a plain if is still better: A single simple boolean check gains nothing from pattern syntax.

csharp

decimal Discount(Customer c) => c switch { { Tier: Gold, Years: > 5 } => 0.2m, { Tier: Gold } => 0.1m, null => throw new ArgumentNullException(), _ => 0m };

Q68.
What are required members (introduced in C# 11), and what problem do they solve for object initialization?

Mid

Required members (C# 11) are properties or fields marked with the required keyword that the compiler forces the caller to set during object initialization. They give you mandatory initialization without writing a constructor for every combination of arguments.

  • The problem they solve:

    • Object initializers are convenient but optional, so a caller could forget a critical property and leave the object half-built.

    • The pre-C#11 fix was hand-written constructors, which become verbose with many properties.

  • How they work:

    • A required member must be assigned in an object initializer or a constructor, or you get a compile-time error.

    • They pair well with init-only setters for immutable-after-construction objects.

    • A constructor can promise to set them using [SetsRequiredMembers], which exempts callers using that constructor.

  • Benefit: enforces invariants at compile time while keeping the readable object-initializer syntax.

csharp

public class User { public required string Name { get; init; } public required string Email { get; init; } public int Age { get; init; } // optional } // Compile error if Name or Email is omitted: var u = new User { Name = "Ana", Email = "ana@x.com" };

Q69.
What is Pattern Matching in C#, and how has it evolved in recent versions?

Mid

Pattern matching in C# is testing a value against a pattern (its type, value, or shape) and optionally binding parts of it, used with is expressions and switch. It has grown from a simple type check in C# 7 into a rich, composable mini-language.

  • C# 7: foundations: Type patterns (is Type x), constant patterns, and case patterns with when guards.

  • C# 8: switch expressions: Concise value-returning switch expressions, plus property, tuple, and positional patterns.

  • C# 9: logical and relational: and, or, not combinators and <, >, <=, >= relational patterns.

  • C# 10 and 11: Extended property patterns ({ Address.City: "x" }) and list/slice patterns ([1, .., 9]).

  • Why it matters: enables declarative, exhaustiveness-checked branching and clean deconstruction of data.

Q70.
What is the Spread Operator in C# collection expressions, and how does it differ from traditional concatenation?

Mid

The spread operator .. (introduced with collection expressions in C# 12) inlines the elements of one collection into a collection expression, so you can flatten several sources and individual items into a single new collection in one literal.

  • Syntax:

    • Inside [ ... ], ..source expands source's elements rather than adding the collection itself as one element.

    • You can mix spreads with literal elements: [0, ..a, 99, ..b].

  • How it differs from traditional concatenation:

    • More concise and readable than Concat chains or manual AddRange calls.

    • Works across many target types (arrays, List<T>, spans) because the target type drives how it is built.

    • The result is a single new collection, not a lazy/deferred IEnumerable wrapper like Concat.

  • Note: spread enumerates each source to copy its elements, so it is materialization, not a view.

csharp

int[] a = [1, 2, 3]; int[] b = [4, 5]; // Spread: flatten both plus a literal into one array int[] all = [..a, ..b, 6]; // [1,2,3,4,5,6] // Traditional equivalent int[] old = a.Concat(b).Append(6).ToArray();

Q71.
Explain the difference between JIT (Just-In-Time) compilation and AOT (Ahead-Of-Time) compilation. What are the tradeoffs of Native AOT?

Senior

JIT compiles IL to native code at runtime as methods are first called, while AOT compiles to native code ahead of time during the build. Native AOT trades runtime adaptability and some compatibility for faster startup, lower memory use, and a self-contained executable with no runtime JIT.

  • JIT compilation:

    • Ships portable IL; the runtime compiles each method to machine code on first use.

    • Can optimize for the actual hardware and use runtime feedback (tiered compilation), but pays a warm-up cost at startup.

  • AOT compilation: Produces native machine code at build time, so there's little or no compilation at runtime.

  • Native AOT tradeoffs (pros):

    • Fast startup and lower memory footprint: ideal for CLIs, serverless, and containers.

    • Single self-contained native binary with no JIT or full runtime needed.

  • Native AOT tradeoffs (cons):

    • No runtime code generation: features relying on reflection/emit or dynamic loading break or need trimming-safe alternatives.

    • Larger build complexity, platform-specific output, and loss of JIT's adaptive optimizations.

Q72.
What is the role of the Global Assembly Cache (GAC) in modern .NET (Core) vs. .NET Framework?

Senior

The GAC is a machine-wide store of shared, strong-named assemblies used by .NET Framework; modern .NET (Core and later) abandoned it in favor of app-local dependencies for isolation and side-by-side versioning.

  • In .NET Framework:

    • A central cache where multiple apps share one copy of a strong-named assembly.

    • Supports side-by-side versions and was managed with tools like gacutil.

    • Downside: "DLL hell" risk when a shared update broke other apps.

  • In modern .NET:

    • No GAC; each app deploys its own dependencies (app-local), often via NuGet.

    • Gives clean side-by-side execution and self-contained deployments, so two apps can use different versions safely.

    • The shared framework lives in a runtime folder, not a versioned global cache for arbitrary libraries.

Q73.
Explain the 'Diamond Problem' and how C# handles multiple inheritance through interfaces.

Senior

The Diamond Problem arises when a type inherits from two parents that share a common base, creating ambiguity over which inherited implementation to use. C# sidesteps it by forbidding multiple class inheritance and instead allowing multiple interface implementation, where (traditionally) the implementing class supplies the single concrete behavior.

  • The classic diamond: D inherits from B and C, both of which inherit from A; if both override a member, which one does D get? Languages with multiple class inheritance must resolve this ambiguity.

  • C#'s rule:

    • A class has exactly one base class, so there is no ambiguity of inherited state/implementation.

    • A class may implement many interfaces, which traditionally carried only contracts (no implementation), so there was nothing to conflict.

  • Resolving interface name clashes: If two interfaces declare the same member, use explicit interface implementation to provide a distinct body for each.

  • The modern wrinkle: Default interface methods (C# 8) reintroduce a limited diamond risk; the compiler requires you to resolve ambiguous defaults explicitly, often by re-implementing the member.

csharp

interface ILeft { void Save(); } interface IRight { void Save(); } class Document : ILeft, IRight { void ILeft.Save() { /* left behavior */ } void IRight.Save() { /* right behavior */ } }

Q74.
How do Default Interface Methods work, and why were they added to the language?

Senior

Default interface methods (DIMs), introduced in C# 8, let an interface declare a method body, so implementers inherit that behavior unless they override it. They were added mainly so library authors can add members to existing interfaces without breaking every type that already implements them.

  • How they work:

    • The interface provides a concrete body; a class implementing the interface gets that implementation for free.

    • The default is only callable through the interface reference, not directly via the class instance, unless the class re-declares it.

  • Why they were added:

    • API evolution: previously adding a member to a published interface broke all implementers; a default body keeps them compiling.

    • Enables trait-like sharing of behavior across implementers.

  • Caveats:

    • Interfaces still cannot hold instance state (no instance fields).

    • Reintroduces a controlled diamond problem: ambiguous defaults from multiple interfaces must be resolved explicitly.

csharp

interface ILogger { void Log(string msg); // default method: existing implementers don't have to write it void LogError(string msg) => Log($"ERROR: {msg}"); }

Q75.
What is Variance (Covariance and Contravariance) in C# Generics? Why can't you pass a List<string> into a method expecting a List<object>?

Senior

Variance lets you substitute related generic types when it is provably safe: covariance (out) allows a more derived type to flow out, contravariance (in) allows a more derived type to flow in. List<T> is invariant because it both reads and writes T, so allowing the substitution would break type safety.

  • Covariance (out T): Used when T only appears in output positions; e.g. IEnumerable<string> is assignable to IEnumerable<object>.

  • Contravariance (in T): Used when T only appears in input positions; e.g. an IComparer<object> can be used where IComparer<string> is expected.

  • Why List<string> cannot pass as List<object>:

    • If it could, the method could call list.Add(new Cat()) on what is really a list of strings, corrupting it.

    • Because Add (input) and indexer reads (output) both use T, neither in nor out is safe, so it is invariant.

  • Practical note:

    • Variance only applies to interfaces and delegates, and only to reference types; it never applies to concrete classes like List<T>.

    • Accept IEnumerable<object> in your method signature to take a List<string> read-only.

Q76.
What are Interceptors in C# 12, and what is their primary use case in source generators?

Senior

Interceptors are a C# 12 (preview) feature that lets a source generator declare that a method call at a specific source location should be redirected to a different, generated method at compile time. Their main purpose is to let source generators replace or specialize existing calls without the developer changing their code.

  • How they work:

    • A generated method is marked with [InterceptsLocation] pointing at the file, line, and column of the original call.

    • At compile time the compiler rewrites that exact call to invoke the interceptor instead.

  • Primary use case:

    • Source generators can swap a generic/reflection-based call for a fast, pre-baked specialized version (e.g. AOT-friendly serialization, minimal API route handling).

    • Enables optimization without runtime reflection and without forcing the user to rewrite their code.

  • Caveats:

    • Tied to exact source positions, so they are fragile to edits; intended for tooling, not hand-written code.

    • Experimental and opt-in via a feature flag; semantics may change.

Q77.
What are Source Generators, and how do they differ from Reflection in terms of performance and timing?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q78.
What is a closure in C#, and how does variable capture in a lambda work?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q79.
How does a Dictionary<K, V> work internally? What happens during a hash collision?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q80.
What are Frozen Collections (introduced in .NET 8), and when would you use them over a standard Dictionary or HashSet?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q81.
What is a WeakReference, and when would you use one?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q82.
Explain how the async and await keywords work under the hood. What is the state machine?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q83.
What is the difference between a Task and a ValueTask, and when should you prefer one over the other?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q84.
Explain the purpose of ConfigureAwait(false). When is it necessary, and when should it be avoided?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q85.
What is the difference between Task.Run() and Task.Factory.StartNew()?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q86.
What is a deadlock in an asynchronous context, and how can you prevent it?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q87.
What is the new System.Threading.Lock type in .NET 9, and how does it differ from lock(obj)?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q88.
Why is async void generally considered a bad practice, and what are the rare exceptions where it is acceptable?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q89.
Explain IAsyncEnumerable<T> and how it differs from a standard Task<IEnumerable<T>>.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q90.
How does the lock statement work internally? What is the difference between lock and Monitor?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q91.
What does the volatile keyword do, and when is it needed?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q92.
What is the difference between lock, Mutex, and SemaphoreSlim for synchronization?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q93.
How does the .NET Garbage Collector work? Explain the concept of Generations (0, 1, and 2).

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q94.
What is the Large Object Heap (LOH), and why is it handled differently by the GC?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q95.
What is Reflection, and what are the performance implications of using it extensively?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q96.
What are Span<T> and Memory<T>, and what specific problem do they solve regarding memory allocation?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q97.
What is 'pinning' an object in memory, and when would you need to do it?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q98.
When would you use GC.Collect(), and what are the trade-offs of calling it manually?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q99.
Explain the difference between the Small Object Heap (SOH) and the Large Object Heap (LOH). Why is LOH fragmentation a concern?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q100.
Explain the concept of ref struct like Span<T>. Why can it only exist on the stack, and what are its limitations?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q101.
Explain the 'params collections' feature in C# 13. How does it improve over the old params array?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q102.
Explain the field keyword introduced in C# 13 and how it simplifies property backing fields.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.