[{"data":1,"prerenderedAt":411},["ShallowReactive",2],{"topic-dotnet-fundamentals":3},{"framework":4,"topic":15,"subtopics":23},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fdotnet.yml",".NET Core interview questions on the CLR, C# language features, ASP.NET Core, dependency injection, Entity Framework, and deployment — the backbone of modern Microsoft-stack backend development.","yml","dotnet",{},".NET Core",7,"frameworks\u002Fdotnet",1,"_nP5kVwZOMUX9nd4gS5Ozj5Q6zQOEmGtLONE3ue01Gw",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":13,"slug":20,"stem":21,"__hash__":22},"topics\u002Ftopics\u002Fdotnet-fundamentals.yml","Core .NET runtime concepts that come up in every interview — how the CLR works, memory allocation, generics design, LINQ execution, and null safety.",{},"Fundamentals","fundamentals","topics\u002Fdotnet-fundamentals","_b39RHQzqLzHVeyWOxUJPMmbHCdE45y7twngM9qGSzM",[24,110,185,260,336],{"id":25,"title":26,"body":27,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":36,"navigation":37,"order":13,"path":38,"questions":39,"questionsCount":102,"related":103,"seo":104,"seoDescription":105,"stem":106,"subtopic":107,"topic":19,"topicSlug":20,"updated":108,"__hash__":109},"qa\u002Fdotnet\u002Ffundamentals\u002Fclr-runtime.md","Clr Runtime",{"type":28,"value":29,"toc":30},"minimark",[],{"title":31,"searchDepth":32,"depth":32,"links":33},"",2,[],"medium","md",{},true,"\u002Fdotnet\u002Ffundamentals\u002Fclr-runtime",[40,45,49,53,57,61,65,69,73,77,81,85,89,94,98],{"id":41,"difficulty":42,"q":43,"a":44},"what-is-clr","easy","What is the CLR and what does it do?","The **Common Language Runtime (CLR)** is the virtual machine at the heart of .NET.\nIt takes compiled **Intermediate Language (IL)** bytecode and executes it on the\nhost OS, providing services that would otherwise be the developer's responsibility.\n\n```csharp\n\u002F\u002F You write C# → the compiler produces IL → the CLR executes IL\n\u002F\u002F dotnet run triggers: csc → assembly.dll (IL) → CLR JIT → native CPU instructions\nConsole.WriteLine(\"Hello from managed code\"); \u002F\u002F CLR handles memory, exceptions, types\n```\n\nThe CLR provides: **JIT compilation** (IL → native code), **garbage collection**\n(automatic memory management), **type safety** enforcement, **exception handling**,\n**thread management**, and **security sandboxing**. Every .NET language (C#, F#,\nVB.NET) compiles to the same IL, so the CLR is truly language-agnostic.\n\n**Rule of thumb:** The CLR is to .NET what the JVM is to Java — an execution engine\nthat abstracts the OS and hardware while enforcing language rules at runtime.\n",{"id":46,"difficulty":34,"q":47,"a":48},"what-is-il","What is Intermediate Language (IL) and how does the CLR execute it?","**Intermediate Language (IL)**, also called **CIL** (Common Intermediate Language)\nor **MSIL**, is the CPU-agnostic bytecode that .NET compilers produce. It is a\nstack-based instruction set — not native machine code, not source code.\n\n```csharp\n\u002F\u002F Source:\nint Add(int a, int b) => a + b;\n\n\u002F\u002F IL (roughly, as shown by ildasm \u002F dnSpy):\n\u002F\u002F ldarg.1   — push 'a' onto evaluation stack\n\u002F\u002F ldarg.2   — push 'b' onto evaluation stack\n\u002F\u002F add       — pop two values, push their sum\n\u002F\u002F ret       — return top of stack\n```\n\nWhen the runtime first calls a method, the **JIT compiler** translates that method's\nIL to native CPU instructions for the current machine. The result is cached for the\nprocess lifetime — the JIT only runs once per method per process. You can inspect\nIL with `dotnet tool install -g dotnet-ildasm` or view it in dnSpy\u002FILSpy.\n\n**Rule of thumb:** IL is the \"assembly language\" of .NET — write once in any CLS\nlanguage, run anywhere the CLR is installed, on any CPU architecture.\n",{"id":50,"difficulty":34,"q":51,"a":52},"jit-vs-aot","What is JIT compilation and how does it differ from AOT?","**JIT (Just-In-Time)** compiles IL to native code at runtime, method by method,\nthe first time each method is called. **AOT (Ahead-Of-Time)** compiles everything\nto native code before the app starts.\n\n```csharp\n\u002F\u002F JIT (default): compilation happens at runtime\n\u002F\u002F First call to Foo() triggers JIT; subsequent calls use cached native code\n\n\u002F\u002F AOT via .NET Native Ahead-of-Time (PublishAot = true in .csproj):\n\u002F\u002F \u003CPublishAot>true\u003C\u002FPublishAot>\n\u002F\u002F dotnet publish -r linux-x64 -c Release\n\u002F\u002F Result: single self-contained native binary, no CLR required at runtime\n```\n\n| | JIT | AOT |\n|---|---|---|\n| Startup | Slower (JIT work on first calls) | Fast (native binary) |\n| Peak perf | High (tiered JIT optimises hot paths) | High but fixed |\n| Deploy size | Needs .NET runtime installed | Larger self-contained binary |\n| Use case | Long-running services | CLI tools, containers, IoT |\n\n.NET 6+ also has **Tiered Compilation**: methods start with a quick \"tier 0\" compile,\nthen hot methods are recompiled with full optimisations at \"tier 1\".\n\n**Rule of thumb:** Use JIT for servers and services (runtime profiling = better\noptimisation); use AOT for CLI tools and latency-sensitive cold starts.\n",{"id":54,"difficulty":34,"q":55,"a":56},"common-type-system","What is the Common Type System (CTS)?","The **Common Type System (CTS)** defines every data type that the CLR understands\nand the rules for declaring, using, and managing those types. It ensures that objects\nwritten in different .NET languages can interact without type-mismatch errors.\n\n```csharp\n\u002F\u002F C# int → CTS System.Int32\n\u002F\u002F VB.NET Integer → CTS System.Int32\n\u002F\u002F F# int → CTS System.Int32\n\u002F\u002F All three are the exact same CLR type — fully interoperable\n\nint x = 42;                   \u002F\u002F C# keyword\nSystem.Int32 y = 42;          \u002F\u002F CTS type name — identical at runtime\nConsole.WriteLine(x.GetType().FullName); \u002F\u002F \"System.Int32\"\n```\n\nThe CTS defines two top-level categories: **value types** (derive from\n`System.ValueType`, stored by value) and **reference types** (derive from\n`System.Object`, stored as references). Every type in every .NET language must\nconform to these rules, which is why a C# `List\u003Cint>` can be consumed by F# code\nwithout any wrapper.\n\n**Rule of thumb:** The CTS is the shared type contract between all .NET languages —\nit is what makes cross-language .NET libraries possible.\n",{"id":58,"difficulty":34,"q":59,"a":60},"cls-common-language-specification","What is the Common Language Specification (CLS) and why does it matter?","The **Common Language Specification (CLS)** is a subset of the CTS that defines the\nminimum set of features a .NET language must support to be interoperable with any\nother CLS-compliant language. It is more restrictive than the full CTS.\n\n```csharp\n\u002F\u002F CLS-non-compliant: unsigned integers are not in the CLS\n\u002F\u002F (VB.NET doesn't have uint, so a public API using uint breaks interop)\npublic uint GetCount() => 42u;  \u002F\u002F not CLS-compliant\n\n\u002F\u002F CLS-compliant fix:\npublic int GetCount() => 42;    \u002F\u002F all CLS languages support int (System.Int32)\n\n\u002F\u002F Mark your assembly as CLS-compliant to get compiler warnings:\n[assembly: CLSCompliant(true)]\n```\n\nFeatures excluded from the CLS include: unsigned integer types in public APIs,\nglobal functions, pointer types, operator overloading in certain forms, and some\nnaming conventions (CLS is case-insensitive for public identifiers). If you are\nwriting a library for broad .NET language consumption, mark it `[CLSCompliant(true)]`\nand the compiler will flag violations.\n\n**Rule of thumb:** Target CLS compliance for public library APIs; internal code can\nuse the full CTS freely.\n",{"id":62,"difficulty":42,"q":63,"a":64},"managed-vs-unmanaged","What is the difference between managed and unmanaged code?","**Managed code** runs under CLR supervision — the runtime handles memory allocation,\ngarbage collection, type safety, and exception propagation. **Unmanaged code** runs\ndirectly on the OS without CLR oversight — the developer is responsible for memory.\n\n```csharp\n\u002F\u002F Managed: CLR allocates and frees memory automatically\nvar list = new List\u003Cstring>();\nlist.Add(\"hello\");\n\u002F\u002F GC will reclaim list when it's no longer reachable — no manual free()\n\n\u002F\u002F Calling unmanaged code via P\u002FInvoke:\n[DllImport(\"kernel32.dll\")]\nstatic extern IntPtr GetConsoleWindow();\n\n\u002F\u002F Unsafe managed code (pointer arithmetic, must mark block):\nunsafe void ProcessBuffer(byte* ptr, int length)\n{\n    for (int i = 0; i \u003C length; i++)\n        ptr[i] = 0; \u002F\u002F direct memory access — no GC protection here\n}\n```\n\nInterop between managed and unmanaged code uses **P\u002FInvoke** (platform invocation)\nor **COM Interop**. The `unsafe` keyword enables pointer operations within managed\ncode but the CLR still bounds-checks when you re-enter safe territory.\n\n**Rule of thumb:** Write managed code by default; drop to P\u002FInvoke or `unsafe` only\nwhen you need OS APIs, native libraries, or zero-allocation hot paths.\n",{"id":66,"difficulty":34,"q":67,"a":68},"what-is-assembly","What is a .NET Assembly and what does it contain?","A **.NET Assembly** is the compiled, deployable unit of .NET code — typically a\n`.dll` or `.exe` file. It is the fundamental unit of versioning, deployment, and\nsecurity in .NET.\n\n```csharp\n\u002F\u002F View assembly info at runtime:\nvar asm = typeof(string).Assembly;\nConsole.WriteLine(asm.FullName);\n\u002F\u002F \"System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\"\n\n\u002F\u002F Load an assembly dynamically:\nvar loaded = Assembly.LoadFrom(\"MyPlugin.dll\");\nvar type = loaded.GetType(\"MyPlugin.EntryPoint\");\n```\n\nAn assembly contains: **IL bytecode** (the compiled code), a **manifest** (name,\nversion, culture, strong-name public key, list of referenced assemblies), **metadata**\n(type descriptions used by reflection), and optionally **resources** (embedded images,\nstrings). A single assembly can span multiple source files but compiles to one binary.\nMulti-file assemblies are rare in .NET Core.\n\n**Rule of thumb:** One project → one assembly. The assembly manifest is what NuGet\nand the runtime use to resolve the right version of a dependency.\n",{"id":70,"difficulty":42,"q":71,"a":72},"dotnet-versions","What is the difference between .NET Framework, .NET Core, and modern .NET (5+)?","**.NET Framework** (2002–2019) is Windows-only, ships with Windows, and is legacy.\n**.NET Core** (2016–2020) was the cross-platform rewrite. **.NET 5+** (2020–present)\nunified both under a single cross-platform runtime, dropping the \"Core\" suffix.\n\n```xml\n\u003C!-- .csproj target framework monikers -->\n\u003CTargetFramework>net48\u003C\u002FTargetFramework>       \u003C!-- .NET Framework 4.8, Windows only -->\n\u003CTargetFramework>netcoreapp3.1\u003C\u002FTargetFramework> \u003C!-- .NET Core 3.1, LTS, EOL 2022 -->\n\u003CTargetFramework>net8.0\u003C\u002FTargetFramework>       \u003C!-- .NET 8 (current LTS), cross-platform -->\n\u003CTargetFramework>net9.0\u003C\u002FTargetFramework>       \u003C!-- .NET 9 (current STS) -->\n```\n\n| | .NET Framework | .NET Core \u002F .NET 5+ |\n|---|---|---|\n| Platform | Windows only | Windows, Linux, macOS |\n| Open source | No | Yes |\n| Side-by-side | No (machine-wide) | Yes (multiple versions per machine) |\n| New features | None (frozen) | Yes (annual releases) |\n\n.NET follows a predictable cadence: even-numbered versions (6, 8, 10) are **LTS**\n(3-year support); odd-numbered (7, 9) are **STS** (18-month support).\n\n**Rule of thumb:** Always target the latest LTS for new projects; migrate .NET\nFramework apps when you need Linux\u002Fcontainer deployment or modern C# features.\n",{"id":74,"difficulty":34,"q":75,"a":76},"garbage-collection-overview","How does the .NET garbage collector work at a high level?","The .NET **Garbage Collector (GC)** is a generational, tracing collector. It\nperiodically identifies objects no longer reachable from any **GC root** (static\nfields, local variables, CPU registers) and reclaims their memory.\n\n```csharp\n\u002F\u002F GC roots keep objects alive:\nstatic List\u003Cstring> _cache = new();   \u002F\u002F static field — GC root, always alive\nvoid ProcessRequest()\n{\n    var data = new byte[1024];         \u002F\u002F local var — GC root while method runs\n    _cache.Add(\"entry\");              \u002F\u002F added to static — stays alive\n    \u002F\u002F 'data' goes out of scope here — eligible for collection next GC cycle\n}\n\n\u002F\u002F Force a collection (rarely appropriate in production):\nGC.Collect(2, GCCollectionMode.Forced, blocking: true);\n```\n\nThe GC uses a **mark-and-compact** algorithm: mark all live objects, then compact\nthe heap by sliding live objects together (eliminating fragmentation). Large objects\n(>85 KB by default) go to the **Large Object Heap (LOH)** which is swept but not\ncompacted by default. The GC can run concurrently with your code in **background GC**\nmode to minimise pause times.\n\n**Rule of thumb:** Don't fight the GC — avoid unnecessary allocations, use `ArrayPool\u003CT>`\nfor large buffers, and implement `IDisposable` only for unmanaged resources.\n",{"id":78,"difficulty":34,"q":79,"a":80},"gc-generations","What are GC generations and why do they matter?","The .NET GC divides the managed heap into three **generations** (Gen 0, Gen 1, Gen 2)\nbased on the observation that most objects die young. Collecting younger generations\nis cheaper because they are smaller.\n\n```csharp\n\u002F\u002F Check which generation an object is in:\nvar obj = new object();\nConsole.WriteLine(GC.GetGeneration(obj)); \u002F\u002F 0 — just allocated\n\nGC.Collect(0); \u002F\u002F collect Gen 0 only (cheapest)\n\u002F\u002F If 'obj' survived, it is now promoted to Gen 1\nConsole.WriteLine(GC.GetGeneration(obj)); \u002F\u002F 1\n\nGC.Collect(1); \u002F\u002F collect Gen 0 + Gen 1\nConsole.WriteLine(GC.GetGeneration(obj)); \u002F\u002F 2 — fully tenured\n```\n\n**Gen 0** — new allocations; collected frequently (milliseconds). **Gen 1** — objects\nthat survived one Gen 0 collection; a buffer zone collected less often. **Gen 2** —\nlong-lived objects (static-like); collected rarely, most expensive. A **Gen 2**\ncollection (full GC) can cause noticeable pauses in latency-sensitive apps.\n\nMemory pressure on Gen 2 is a common production issue — typically caused by excessive\ncaching, long-lived `string` allocations, or `IDisposable` objects not being disposed.\n\n**Rule of thumb:** Short-lived objects (request-scoped) are cheap; long-lived\nobjects (Gen 2) cost GC pressure — minimise how much data you promote to Gen 2.\n",{"id":82,"difficulty":34,"q":83,"a":84},"reflection","What is reflection in .NET and when should you use it?","**Reflection** is the ability of .NET code to inspect and invoke type metadata —\nclass names, methods, properties, attributes — at runtime without knowing them at\ncompile time. It is provided by the `System.Reflection` namespace.\n\n```csharp\nusing System.Reflection;\n\nvar type = typeof(DateTime);\n\n\u002F\u002F List all public instance methods:\nforeach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))\n    Console.WriteLine(method.Name);\n\n\u002F\u002F Invoke a private method dynamically:\nvar mi = type.GetMethod(\"InternalTicksToDateTime\",\n                        BindingFlags.NonPublic | BindingFlags.Static);\n\u002F\u002F mi?.Invoke(null, new object[] { ticks });\n\n\u002F\u002F Read a custom attribute:\nvar attr = typeof(MyService).GetCustomAttribute\u003CObsoleteAttribute>();\nConsole.WriteLine(attr?.Message);\n```\n\nReflection is used by: **dependency injection containers** (scanning for constructors),\n**serialisers** (JSON.NET, System.Text.Json), **ORMs** (EF Core mapping), **test\nframeworks** (discovering `[Test]` methods), and **plugin systems**. The downsides are\nperformance overhead and loss of compile-time type safety.\n\n**Rule of thumb:** Let frameworks use reflection internally; avoid it in your own\nhot paths. Prefer source generators (C# 9+) for compile-time code generation as a\nzero-overhead alternative.\n",{"id":86,"difficulty":34,"q":87,"a":88},"stack-vs-heap","What is the difference between stack and heap allocation in the CLR?","The CLR manages two primary memory regions. The **stack** is a per-thread, LIFO\nstructure for method frames, local variables of value types, and return addresses.\nThe **heap** is a shared, GC-managed area for all reference-type objects.\n\n```csharp\nvoid Example()\n{\n    int x = 10;           \u002F\u002F value type local — lives on the STACK for this frame\n    var s = \"hello\";      \u002F\u002F string is a reference type — object on HEAP, reference on stack\n    var p = new Person(); \u002F\u002F Person object on HEAP, 'p' reference on stack\n}                         \u002F\u002F frame pops: x, s, p references gone; GC eventually frees heap objects\n\nstruct Point { public int X, Y; }   \u002F\u002F value type\nclass Circle { public Point Center; } \u002F\u002F Circle on heap; Center (struct) embedded in it\n```\n\nValue types declared as **fields of a class** live on the heap — inside the object\nthat contains them. \"Value types live on the stack\" is a simplification true only for\nlocal variables and parameters. `Span\u003CT>` and `ref struct` types are designed to be\nstack-only to enable safe, zero-copy buffer operations.\n\n**Rule of thumb:** Stack allocation is free and deterministic; heap allocation\nincurs GC pressure. For hot loops, prefer value types and `Span\u003CT>` to avoid heap\nallocations.\n",{"id":90,"difficulty":91,"q":92,"a":93},"tiered-compilation","hard","What is tiered compilation in .NET and how does it improve performance?","**Tiered compilation** (enabled by default since .NET Core 3.0) allows the JIT to\ncompile methods multiple times at increasing optimisation levels, trading startup\nspeed for peak throughput.\n\n```csharp\n\u002F\u002F You write:\nint Sum(int a, int b) => a + b;\n\n\u002F\u002F Tier 0 (first call): compiled quickly with minimal optimisation\n\u002F\u002F → fast startup, no inlining, no loop unrolling\n\n\u002F\u002F After the method is called enough times (instrumented with call counters):\n\u002F\u002F Tier 1 (hot method): recompiled with full optimisation\n\u002F\u002F → inlined, loop-unrolled, register-allocated aggressively\n\n\u002F\u002F Disable via environment variable (for benchmarking):\n\u002F\u002F DOTNET_TieredCompilation=0\n```\n\nTier 0 uses **instrumentation stubs** to count calls. Once a method crosses a\nthreshold, the runtime queues it for **tier 1** recompilation on a background thread.\nThe old tier 0 code stays active until tier 1 is ready, then the call-site patch\nis swapped atomically.\n\n**ReadyToRun (R2R)** is a related feature that pre-compiles IL to native code at\npublish time, providing faster startup (like AOT) while still allowing tier 1\nrecompilation at runtime for peak performance.\n\n**Rule of thumb:** Tiered compilation is transparent — leave it on. If you're\nmicro-benchmarking (BenchmarkDotNet), disable it or let the benchmark harness warm\nup properly before measuring.\n",{"id":95,"difficulty":34,"q":96,"a":97},"appdomains-and-isolation","What is an AppDomain and how does process isolation work in modern .NET?","An **AppDomain** was the .NET Framework mechanism for isolating multiple logical\napplications within a single process. It provided separate heaps, separate type\nsystems, and remoting between domains. In **.NET Core and .NET 5+**, AppDomains are\nlargely removed — there is only one AppDomain per process.\n\n```csharp\n\u002F\u002F .NET Framework: create a sandbox domain (no longer works in .NET Core)\n\u002F\u002F var domain = AppDomain.CreateDomain(\"Sandbox\");\n\u002F\u002F domain.ExecuteAssembly(\"plugin.dll\");\n\n\u002F\u002F .NET 5+ equivalent: AssemblyLoadContext for plugin isolation\nvar context = new System.Runtime.Loader.AssemblyLoadContext(\"Plugin\", isCollectible: true);\nvar asm = context.LoadFromAssemblyPath(\"\u002Fpath\u002Fto\u002Fplugin.dll\");\nvar type = asm.GetType(\"Plugin.EntryPoint\");\n\u002F\u002F ... invoke methods via reflection ...\n\n\u002F\u002F Unload the context (and all assemblies in it) when done:\ncontext.Unload();\n\n\u002F\u002F True process isolation still requires separate processes:\n\u002F\u002F var proc = Process.Start(new ProcessStartInfo(\"dotnet\", \"plugin.dll\"));\n```\n\n**AssemblyLoadContext (ALC)** is the modern replacement: it provides assembly\nisolation and — when marked `isCollectible: true` — the ability to unload\nassemblies, which is crucial for hot-reload plugin systems.\n\n**Rule of thumb:** Forget AppDomains for .NET Core\u002F5+ work. Use\n`AssemblyLoadContext` for plugin loading and unloading. For true security\nisolation, use separate processes or containers.\n",{"id":99,"difficulty":34,"q":100,"a":101},"clr-exceptions","How does the CLR handle exceptions — what happens when an exception is thrown?","When an exception is thrown, the CLR unwinds the call stack looking for a matching\n`catch` handler. If none is found, the process terminates (with an unhandled-exception\nevent fired first). The mechanism relies on **Structured Exception Handling (SEH)**\nat the OS level on Windows and equivalent mechanisms on Linux\u002FmacOS.\n\n```csharp\nvoid Outer()\n{\n    try\n    {\n        Inner();\n    }\n    catch (InvalidOperationException ex)\n    {\n        \u002F\u002F CLR walked up the stack, found this handler\n        Console.WriteLine(ex.Message);\n    }\n    finally\n    {\n        \u002F\u002F Always runs — even if no catch matched, after the handler runs\n        Console.WriteLine(\"cleanup\");\n    }\n}\n\nvoid Inner()\n{\n    \u002F\u002F Note: rethrowing with 'throw' preserves the original stack trace\n    \u002F\u002F Rethrowing with 'throw ex' resets the stack trace — avoid that\n    try { DoWork(); }\n    catch { throw; } \u002F\u002F Good: preserves original stack trace\n}\n```\n\nThe CLR distinguishes **first-chance exceptions** (before any handler runs — useful\nfor debuggers) from **second-chance exceptions** (unhandled). The `finally` block\nruns after the matching `catch` block, or during stack unwinding if no handler\nis found in the current frame.\n\n**Rule of thumb:** Always rethrow with bare `throw`, never `throw ex` — the latter\ndestroys the original stack trace. Use `finally` for cleanup, not to swallow\nexceptions.\n",15,null,{"description":31},"How the .NET CLR executes managed code — IL, JIT compilation, garbage collection, the Common Type System, and the assembly model.","dotnet\u002Ffundamentals\u002Fclr-runtime","CLR Runtime","2026-06-22","VAonIsovnk1_RDbbYm1pGfCC2Krl14sKnbIOEMux4bY",{"id":111,"title":112,"body":113,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":117,"navigation":37,"order":32,"path":118,"questions":119,"questionsCount":102,"related":103,"seo":180,"seoDescription":181,"stem":182,"subtopic":183,"topic":19,"topicSlug":20,"updated":108,"__hash__":184},"qa\u002Fdotnet\u002Ffundamentals\u002Fvalue-vs-reference-types.md","Value Vs Reference Types",{"type":28,"value":114,"toc":115},[],{"title":31,"searchDepth":32,"depth":32,"links":116},[],{},"\u002Fdotnet\u002Ffundamentals\u002Fvalue-vs-reference-types",[120,124,128,132,136,140,144,148,152,156,160,164,168,172,176],{"id":121,"difficulty":42,"q":122,"a":123},"value-vs-reference-basics","What is the difference between value types and reference types in C#?","**Value types** hold their data directly. **Reference types** hold a pointer (reference)\nto data stored on the managed heap. Assigning a value type copies the data; assigning\na reference type copies only the reference — both variables then point to the same object.\n\n```csharp\n\u002F\u002F Value type — copy semantics\nint a = 5;\nint b = a;   \u002F\u002F b gets a copy of 5\nb = 99;\nConsole.WriteLine(a); \u002F\u002F 5 — a is unaffected\n\n\u002F\u002F Reference type — shared reference\nvar list1 = new List\u003Cint> { 1, 2, 3 };\nvar list2 = list1;   \u002F\u002F list2 points to the SAME list\nlist2.Add(4);\nConsole.WriteLine(list1.Count); \u002F\u002F 4 — list1 sees the change\n```\n\nValue types: `int`, `double`, `bool`, `char`, `decimal`, `DateTime`, `struct`, `enum`.\nReference types: `class`, `string`, `object`, arrays, delegates, interfaces.\n`string` behaves like a value type in practice because it is **immutable** — any\n\"change\" creates a new string object.\n\n**Rule of thumb:** If you need copy-on-assign semantics and the data is small and\nshort-lived, use a value type (struct). For anything polymorphic, large, or long-lived,\nuse a class.\n",{"id":125,"difficulty":34,"q":126,"a":127},"stack-vs-heap-storage","Where are value types and reference types stored in memory?","The common interview answer — \"value types on the stack, reference types on the heap\"\n— is an oversimplification. What matters is **how the variable is declared**.\n\n```csharp\nvoid Method()\n{\n    int x = 10;             \u002F\u002F local value type → stack frame\n    object obj = new();     \u002F\u002F 'new object' → heap; reference 'obj' → stack\n}\n\nclass Wrapper\n{\n    int field = 42;         \u002F\u002F value type field inside a class → HEAP (embedded in the object)\n    string name = \"hello\";  \u002F\u002F reference type field → heap (reference stored in object, string on heap)\n}\n\n\u002F\u002F Struct stored as a field of a class lives on the heap inside the class:\nclass Circle { public System.Drawing.Point Center; } \u002F\u002F Point (struct) is on heap\n```\n\nLocal variables of value types live on the **stack** of the current method frame.\nValue type **fields inside a class** live on the **heap** as part of the object.\nReference types always allocate their data on the **heap**; the variable (reference)\nitself can be on either stack or heap depending on context.\n\n**Rule of thumb:** Think \"where is the variable declared?\" not \"what type is it?\" —\nthe location follows the container, not the type.\n",{"id":129,"difficulty":34,"q":130,"a":131},"boxing-unboxing","What is boxing and unboxing, and why is it a performance concern?","**Boxing** wraps a value type in a heap-allocated `System.Object` wrapper. **Unboxing**\nextracts the value type back out. Both operations are implicit but incur hidden costs.\n\n```csharp\nint x = 42;\nobject boxed = x;         \u002F\u002F BOXING: heap allocation + copy\nint unboxed = (int)boxed; \u002F\u002F UNBOXING: type check + copy\n\n\u002F\u002F Hidden boxing in older APIs:\nvar al = new ArrayList();\nal.Add(42);               \u002F\u002F boxes 42 to object — heap allocation!\nint v = (int)al[0];       \u002F\u002F unboxes — type check + copy\n\n\u002F\u002F Generic collections avoid boxing entirely:\nvar list = new List\u003Cint>();\nlist.Add(42);             \u002F\u002F no boxing — int stored directly\nint v2 = list[0];         \u002F\u002F no unboxing\n```\n\nBoxing costs: a **heap allocation**, a **memory copy**, and eventual **GC pressure**.\nIn a tight loop adding thousands of ints to an `ArrayList`, this adds up significantly.\nString interpolation pre-.NET 6 also caused boxing for value types passed as `object`.\nThe fix is always **generics** — `List\u003Cint>` instead of `ArrayList`, `IComparable\u003CT>`\ninstead of `IComparable`.\n\n**Rule of thumb:** Avoid boxing in hot paths. If you see `object` parameters or\nnon-generic collections in performance-sensitive code, that's a boxing smell.\n",{"id":133,"difficulty":42,"q":134,"a":135},"struct-vs-class","What is the difference between a struct and a class in C#?","A **struct** is a value type; a **class** is a reference type. The difference\ndetermines copy semantics, memory layout, inheritance rules, and GC behavior.\n\n```csharp\nstruct Point { public int X, Y; }   \u002F\u002F value type\nclass Circle { public int Radius; } \u002F\u002F reference type\n\nvar p1 = new Point { X = 1, Y = 2 };\nvar p2 = p1;   \u002F\u002F copy — p2 is independent\np2.X = 99;\nConsole.WriteLine(p1.X); \u002F\u002F 1 — unaffected\n\nvar c1 = new Circle { Radius = 5 };\nvar c2 = c1;   \u002F\u002F shared reference\nc2.Radius = 99;\nConsole.WriteLine(c1.Radius); \u002F\u002F 99 — both see the change\n```\n\nKey differences:\n| | struct | class |\n|---|---|---|\n| Type | Value type | Reference type |\n| Default | Zero-initialised, no null | null |\n| Inheritance | Cannot inherit (can implement interfaces) | Full inheritance |\n| `new` | Stack-allocated (if local) | Always heap-allocated |\n| `IDisposable` | Valid but uncommon | Common |\n\n**Rule of thumb:** Use struct for small (≤16 bytes), immutable, logically\nvalue-like data (coordinates, colours, `Guid`). Use class for everything else.\n",{"id":137,"difficulty":34,"q":138,"a":139},"when-to-use-struct","When should you use a struct instead of a class?","Prefer a **struct** when the type is small, immutable, frequently copied in bulk\n(e.g., array of coordinates), and semantically represents a single value rather\nthan an entity with identity.\n\n```csharp\n\u002F\u002F Good struct candidate: small, immutable, value semantics\nreadonly struct Vector2\n{\n    public readonly float X, Y;\n    public Vector2(float x, float y) => (X, Y) = (x, y);\n    public float Length => MathF.Sqrt(X * X + Y * Y);\n}\n\n\u002F\u002F Bad struct candidate: large, mutable, reference identity matters\n\u002F\u002F struct HttpClient { ... } \u002F\u002F wrong — should be class\n```\n\nThe CLR can lay out arrays of structs as contiguous memory blocks, which is\nCPU-cache-friendly — a big win for game engines and numerical code. The .NET\nframework uses structs for: `int`, `DateTime`, `Guid`, `KeyValuePair\u003CK,V>`,\n`System.Numerics.Vector3`, and `Span\u003CT>`.\n\nAvoid structs when: the type is larger than ~16 bytes (copying costs outweigh\nallocation savings), it is mutable (mutation bugs are hard to spot with copy\nsemantics), or it needs to be null (use `T?` or a class).\n\n**Rule of thumb:** Default to class. Switch to struct only when profiling shows\nGC pressure from many small, short-lived objects of that type.\n",{"id":141,"difficulty":34,"q":142,"a":143},"pass-by-value","What does \"pass by value\" mean in C#, and how does it apply to reference types?","In C#, **all arguments are passed by value by default** — a copy of the argument\nis made. For value types, the copy is the data itself. For reference types, the\ncopy is the **reference** (pointer), not the object it points to.\n\n```csharp\nvoid MutateList(List\u003Cint> list)\n{\n    list.Add(99);     \u002F\u002F mutates the shared object — caller sees this\n    list = new List\u003Cint>(); \u002F\u002F rebinds local copy of reference — caller doesn't see this\n}\n\nvar nums = new List\u003Cint> { 1, 2 };\nMutateList(nums);\nConsole.WriteLine(nums.Count); \u002F\u002F 3 — Add(99) was visible; reassignment was not\n```\n\n\"Pass by value\" for reference types means the reference is copied. Both the caller\nand callee hold separate copies of the reference that point to the same object.\nMutating the object through the callee's reference is visible to the caller.\nReassigning the callee's reference variable does not affect the caller.\n\n**Rule of thumb:** Pass by value = copy of the handle, not the object. If you need\nto replace the caller's reference itself, use `ref` or return the new value.\n",{"id":145,"difficulty":34,"q":146,"a":147},"ref-keyword","What does the `ref` keyword do when passing parameters?","The `ref` keyword passes a variable **by reference** — the method receives an alias\nto the caller's variable, not a copy. Changes to the parameter directly modify the\ncaller's variable, including reassignment.\n\n```csharp\nvoid Double(ref int value) => value *= 2;\n\nint x = 5;\nDouble(ref x);           \u002F\u002F must use 'ref' at call site too\nConsole.WriteLine(x);   \u002F\u002F 10 — x was modified in-place\n\n\u002F\u002F ref with reference types — can replace the caller's reference:\nvoid ReplaceList(ref List\u003Cint> list)\n{\n    list = new List\u003Cint> { 99 }; \u002F\u002F caller's variable now points to new list\n}\n\nvar nums = new List\u003Cint> { 1, 2 };\nReplaceList(ref nums);\nConsole.WriteLine(nums[0]); \u002F\u002F 99\n```\n\n`ref` is commonly used in high-performance code to avoid copying large structs,\nin `TryGetValue`-style patterns, and in `ref return` \u002F `ref local` to expose\nreferences into arrays or data structures without copying.\n\n**Rule of thumb:** Use `ref` when the method needs to reassign the caller's variable,\nor to avoid copying large structs in performance-critical code.\n",{"id":149,"difficulty":42,"q":150,"a":151},"out-keyword","What is the `out` parameter modifier and how does it differ from `ref`?","`out` is like `ref` — it passes by reference — but with two differences: the\nvariable does **not** need to be initialised before the call, and the method\n**must** assign it before returning.\n\n```csharp\n\u002F\u002F Classic Try-Parse pattern:\nbool success = int.TryParse(\"42\", out int result);\n\u002F\u002F 'result' was not initialised before the call — that's fine with 'out'\nConsole.WriteLine(success ? result : -1); \u002F\u002F 42\n\n\u002F\u002F Inline declaration (C# 7+):\nif (double.TryParse(\"3.14\", out double pi))\n    Console.WriteLine(pi); \u002F\u002F 3.14\n\n\u002F\u002F Discard when you don't need the out value:\nbool isValid = int.TryParse(input, out _);\n```\n\n| | `ref` | `out` |\n|---|---|---|\n| Must be initialised before call | Yes | No |\n| Must be assigned in method | No | Yes |\n| Use case | Two-way data exchange | Return multiple values |\n\n**Rule of thumb:** Use `out` for the Try-Parse \u002F Try-Get pattern where the method\nsignals success\u002Ffailure via return value and returns data via `out`. Use `ref` when\nthe method needs to read the initial value too.\n",{"id":153,"difficulty":34,"q":154,"a":155},"in-keyword","What is the `in` parameter modifier in C#?","The `in` modifier passes a variable by reference but guarantees the method will\n**not modify** it — it is a **read-only reference**. This avoids copying large\nstructs while providing compile-time safety that the callee cannot mutate.\n\n```csharp\nreadonly struct Matrix4x4 { \u002F* 64 bytes *\u002F }\n\n\u002F\u002F Without 'in': 64-byte copy on every call\nfloat Determinant(Matrix4x4 m) => \u002F* ... *\u002F 0f;\n\n\u002F\u002F With 'in': passed by reference, no copy, callee cannot mutate\nfloat Determinant(in Matrix4x4 m) => \u002F* ... *\u002F 0f;\n\nvar mat = new Matrix4x4();\nfloat det = Determinant(in mat); \u002F\u002F 'in' optional at call site for value types\n```\n\n`in` is most valuable for **large structs** in hot paths (rendering, physics,\nlinear algebra). For small structs (≤pointer size), it may actually be slower\nbecause the CPU must dereference the pointer. Benchmark before adding `in` to\nsmall structs.\n\n**Rule of thumb:** Add `in` to struct parameters larger than a pointer (8 bytes\non 64-bit) in performance-sensitive code. Leave it off for small structs and all\nreference types (reference types already pass the pointer, not the object).\n",{"id":157,"difficulty":91,"q":158,"a":159},"ref-struct","What is a `ref struct` in C#?","A `ref struct` is a struct that is **guaranteed to live only on the stack**. The\nCLR enforces this at compile time — you cannot box it, store it as a class field,\nuse it as a generic type argument, or capture it in a lambda or async method.\n\n```csharp\nref struct StackOnly\n{\n    public int Value;\n}\n\n\u002F\u002F Compile-time error — cannot box a ref struct:\n\u002F\u002F object obj = new StackOnly();\n\u002F\u002F Cannot be a field of a class:\n\u002F\u002F class Wrapper { StackOnly s; }\n\u002F\u002F Cannot use in async context:\n\u002F\u002F async Task Foo() { var s = new StackOnly(); await Task.Delay(1); }\n\u002F\u002F Correct use — short-lived, stack-bound:\nvoid Process(Span\u003Cbyte> data)\n{\n    \u002F\u002F Span\u003CT> is itself a ref struct — stack-only\n    var slice = data[..10]; \u002F\u002F fine: slice is also Span\u003CT> (ref struct)\n}\n```\n\nThe primary use case is `Span\u003CT>` and `ReadOnlySpan\u003CT>` — high-performance, safe\nwrappers over contiguous memory (arrays, stack memory, unmanaged memory) that must\nnot outlive their source. `ref struct` is the mechanism that enforces this lifetime.\n\n**Rule of thumb:** `ref struct` is an advanced, performance-focused feature.\nYou'll mostly encounter it as `Span\u003CT>`. Only define your own when you need a\nstack-bound type with lifetime guarantees.\n",{"id":161,"difficulty":91,"q":162,"a":163},"span-t","What is `Span\u003CT>` and why is it a `ref struct`?","`Span\u003CT>` is a **stack-only, type-safe view over a contiguous block of memory**.\nIt can point to a managed array, a stack-allocated buffer, or unmanaged memory —\nwithout copying any data.\n\n```csharp\n\u002F\u002F Over a managed array — no copy:\nint[] arr = { 1, 2, 3, 4, 5 };\nSpan\u003Cint> span = arr.AsSpan(1, 3); \u002F\u002F points to arr[1..3] = {2, 3, 4}\nspan[0] = 99;\nConsole.WriteLine(arr[1]); \u002F\u002F 99 — same memory\n\n\u002F\u002F Stack-allocated buffer — zero heap allocation:\nSpan\u003Cbyte> buffer = stackalloc byte[256];\nbuffer.Fill(0);\n\n\u002F\u002F String parsing without allocations (ReadOnlySpan\u003Cchar>):\nReadOnlySpan\u003Cchar> line = \"2026-06-22\".AsSpan();\nint year = int.Parse(line[..4]); \u002F\u002F parses \"2026\" without allocating a substring\n```\n\n`Span\u003CT>` is a `ref struct` because it stores an interior pointer into managed\nmemory. If it were allowed on the heap (as a class field or boxed value), the GC\ncould move the pointed-to array while the span's pointer still held the old address.\nBeing stack-only ensures the span is always used within the lifetime of its source.\n\n**Rule of thumb:** Use `Span\u003CT>` \u002F `ReadOnlySpan\u003CT>` to eliminate string and array\nslice allocations in hot paths. It is the .NET replacement for `char*` \u002F `byte*` unsafe pointers.\n",{"id":165,"difficulty":42,"q":166,"a":167},"equality-value-vs-reference","How does equality work differently for value types vs reference types in C#?","By default, **value types** compare by content (structural equality); **reference\ntypes** compare by identity (same object in memory). This comes from how `Equals()`\nand `==` are implemented on `System.ValueType` vs `System.Object`.\n\n```csharp\n\u002F\u002F Value type — structural equality by default:\nvar d1 = new DateTime(2026, 1, 1);\nvar d2 = new DateTime(2026, 1, 1);\nConsole.WriteLine(d1 == d2);      \u002F\u002F True — same data\nConsole.WriteLine(d1.Equals(d2)); \u002F\u002F True\n\n\u002F\u002F Reference type — identity equality by default:\nvar p1 = new Person { Name = \"Alice\" };\nvar p2 = new Person { Name = \"Alice\" };\nConsole.WriteLine(p1 == p2);      \u002F\u002F False — different objects\nConsole.WriteLine(p1.Equals(p2)); \u002F\u002F False (unless Equals is overridden)\n\n\u002F\u002F string is a reference type but overrides == for value equality:\nstring s1 = \"hello\", s2 = \"hello\";\nConsole.WriteLine(s1 == s2); \u002F\u002F True — string overrides ==\n```\n\nTo get value equality on a class, override `Equals()` and `GetHashCode()`, and\noptionally overload `==`. **C# 9 records** do this automatically.\n\n**Rule of thumb:** Value types are equal when their data is equal. Reference types\nare equal when they are the same object — override `Equals`\u002F`GetHashCode` to change\nthat, or use a `record` which auto-generates value equality.\n",{"id":169,"difficulty":34,"q":170,"a":171},"readonly-struct","What is a `readonly struct` in C# and when should you use it?","A `readonly struct` is a struct where all fields and auto-properties are implicitly\nread-only. The compiler enforces immutability: you cannot assign to any field after\nconstruction, and calling instance methods on a `readonly struct` variable does not\nrequire a defensive copy.\n\n```csharp\nreadonly struct Temperature\n{\n    public double Celsius { get; }\n    public double Fahrenheit => Celsius * 9 \u002F 5 + 32;\n\n    public Temperature(double celsius) => Celsius = celsius;\n\n    \u002F\u002F Compiler error if you try:\n    \u002F\u002F public void SetCelsius(double c) { Celsius = c; } \u002F\u002F CS1604\n}\n\n\u002F\u002F The compiler no longer emits a defensive copy when calling methods:\nreadonly Temperature t = new Temperature(100);\nConsole.WriteLine(t.Fahrenheit); \u002F\u002F 212 — no defensive copy needed\n```\n\nWithout `readonly`, calling any instance method on a `readonly` local or `in`\nparameter forces the compiler to copy the struct defensively (because the method\nmight mutate it). `readonly struct` removes that copy entirely, which matters for\nhot loops with large structs.\n\n**Rule of thumb:** Mark structs `readonly` whenever all their state is set in the\nconstructor and never mutated. Combine with `in` parameters and `ref readonly`\nreturns for maximum zero-copy performance.\n",{"id":173,"difficulty":34,"q":174,"a":175},"record-types","What are record types in C# and how do they relate to value vs reference type semantics?","**Records** (C# 9+) are a special class or struct syntax that automatically generates\nvalue-based equality, a `ToString()` implementation, and deconstruction. By default,\n`record` (without `struct`) is a **reference type** but behaves with value semantics\nfor equality.\n\n```csharp\n\u002F\u002F record class (C# 9) — reference type with value equality\nrecord Person(string Name, int Age);\n\nvar a = new Person(\"Alice\", 30);\nvar b = new Person(\"Alice\", 30);\nConsole.WriteLine(a == b);      \u002F\u002F True — value equality (generated)\nConsole.WriteLine(ReferenceEquals(a, b)); \u002F\u002F False — different objects\n\n\u002F\u002F Non-destructive mutation with 'with':\nvar older = a with { Age = 31 }; \u002F\u002F creates a new Person, copies other fields\nConsole.WriteLine(older); \u002F\u002F Person { Name = Alice, Age = 31 }\n\n\u002F\u002F record struct (C# 10) — value type with value equality\nrecord struct Point(int X, int Y);\nvar p1 = new Point(1, 2);\nvar p2 = new Point(1, 2);\nConsole.WriteLine(p1 == p2); \u002F\u002F True\n```\n\nRecords also generate a positional constructor and `Deconstruct` method from\nthe primary constructor syntax. They are ideal for DTOs, commands, events, and\nany immutable data-carrying types.\n\n**Rule of thumb:** Use `record class` for immutable reference types that need\nvalue equality (DTOs, domain events). Use `record struct` for small immutable\nvalue types. Use `class` when you need mutable state or inheritance beyond records.\n",{"id":177,"difficulty":91,"q":178,"a":179},"string-interning","What is string interning in .NET and how does it relate to reference type equality?","**String interning** is a runtime optimisation where the CLR maintains a pool of\nunique string literals. When two string literals have the same content, the CLR\nmakes them reference the same object, saving memory. This can cause surprising\nresults when comparing strings with `ReferenceEquals`.\n\n```csharp\nstring a = \"hello\";\nstring b = \"hello\";\n\u002F\u002F Compile-time literal interning: a and b are the SAME object\nConsole.WriteLine(ReferenceEquals(a, b)); \u002F\u002F True — interned\n\nstring c = new string(new[] { 'h','e','l','l','o' }); \u002F\u002F runtime-constructed\nConsole.WriteLine(ReferenceEquals(a, c)); \u002F\u002F False — different object\n\n\u002F\u002F Force interning of a runtime string:\nstring d = string.Intern(c);\nConsole.WriteLine(ReferenceEquals(a, d)); \u002F\u002F True — d is now the interned version\n\n\u002F\u002F Check if a string is already interned without adding it:\nstring? existing = string.IsInterned(\"hello\"); \u002F\u002F returns the interned ref or null\n```\n\nString interning applies automatically to compile-time literals across all\nassemblies. Runtime strings (built from `StringBuilder`, read from DB, etc.) are\nNOT interned by default. Never compare strings with `==` expecting reference\nidentity — always use `==` (which calls `string.Equals`) or `.Equals()`.\n\n**Rule of thumb:** Use `==` or `.Equals()` for string value comparison — never\n`ReferenceEquals`. Interning is a CLR detail, not a correctness tool. Only call\n`string.Intern` when you have profiled and confirmed it reduces memory for a large\nset of repeated strings.\n",{"description":31},"C# value types vs reference types — stack and heap allocation, boxing costs, struct vs class trade-offs, ref parameters, and Span\u003CT>.","dotnet\u002Ffundamentals\u002Fvalue-vs-reference-types","Value vs Reference Types","Yt8M21EN0NFY0pWsYDEUkJ_Z83QUPSeNzNCtboY58ic",{"id":186,"title":187,"body":188,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":192,"navigation":37,"order":193,"path":194,"questions":195,"questionsCount":102,"related":103,"seo":256,"seoDescription":257,"stem":258,"subtopic":187,"topic":19,"topicSlug":20,"updated":108,"__hash__":259},"qa\u002Fdotnet\u002Ffundamentals\u002Fgenerics.md","Generics",{"type":28,"value":189,"toc":190},[],{"title":31,"searchDepth":32,"depth":32,"links":191},[],{},3,"\u002Fdotnet\u002Ffundamentals\u002Fgenerics",[196,200,204,208,212,216,220,224,228,232,236,240,244,248,252],{"id":197,"difficulty":42,"q":198,"a":199},"what-are-generics","What are generics and why were they introduced in C# 2.0?","**Generics** let you write type-safe, reusable code where the concrete type is\nspecified by the caller, not the author. They eliminate the need for casting and\nprevent boxing\u002Funboxing for value types.\n\n```csharp\n\u002F\u002F Pre-generics (C# 1): must cast, no type safety, boxes value types\nArrayList list = new ArrayList();\nlist.Add(42);           \u002F\u002F boxes int → object\nint x = (int)list[0];  \u002F\u002F unboxes + cast — runtime error if wrong type\n\n\u002F\u002F With generics (C# 2+): type-safe, no boxing, no cast\nList\u003Cint> nums = new List\u003Cint>();\nnums.Add(42);           \u002F\u002F no boxing\nint y = nums[0];        \u002F\u002F no cast, compiler guarantees the type\n\n\u002F\u002F Generic method:\nT Max\u003CT>(T a, T b) where T : IComparable\u003CT>\n    => a.CompareTo(b) >= 0 ? a : b;\n\nConsole.WriteLine(Max(3, 7));       \u002F\u002F 7\nConsole.WriteLine(Max(\"cat\", \"dog\")); \u002F\u002F \"dog\"\n```\n\nThe CLR **reifies** generics — `List\u003Cint>` and `List\u003Cstring>` are genuinely\ndistinct types at runtime, each with their own JIT-compiled native code. This\nmeans value-type specialisations get zero-overhead native implementations.\n\n**Rule of thumb:** Prefer generics over `object` parameters, `ArrayList`, or\ncasting. One generic implementation beats N typed duplicates every time.\n",{"id":201,"difficulty":34,"q":202,"a":203},"generic-constraints","What are generic type constraints in C# and what kinds exist?","**Type constraints** restrict what types a caller can substitute for a type\nparameter, unlocking members of that type inside the generic implementation.\n\n```csharp\n\u002F\u002F where T : class     — T must be a reference type\n\u002F\u002F where T : struct    — T must be a non-nullable value type\n\u002F\u002F where T : new()     — T must have a public parameterless constructor\n\u002F\u002F where T : SomeBase  — T must derive from SomeBase\n\u002F\u002F where T : ISomething — T must implement ISomething\n\u002F\u002F where T : notnull   — T must be non-nullable (C# 8+)\n\u002F\u002F where T : unmanaged — T must be an unmanaged type (C# 7.3+)\n\nclass Repository\u003CT> where T : class, new()\n{\n    public T CreateDefault() => new T(); \u002F\u002F new() constraint enables this\n}\n\nT Clamp\u003CT>(T value, T min, T max) where T : IComparable\u003CT>\n{\n    if (value.CompareTo(min) \u003C 0) return min;  \u002F\u002F CompareTo available via constraint\n    if (value.CompareTo(max) > 0) return max;\n    return value;\n}\n\nConsole.WriteLine(Clamp(15, 1, 10)); \u002F\u002F 10\n```\n\nMultiple constraints combine with commas: `where T : class, ISomething, new()`.\nYou can constrain multiple type parameters independently.\n\n**Rule of thumb:** Add constraints only when you need to call members on `T`.\nEach constraint narrows the caller's type choices — start unconstrained and\nadd constraints as the implementation demands them.\n",{"id":205,"difficulty":91,"q":206,"a":207},"covariance","What is covariance in C# generics?","**Covariance** means a generic type with a more derived type argument is assignable\nto a generic type with a less derived type argument. In C#, covariance is expressed\nwith the `out` keyword on an interface or delegate type parameter.\n\n```csharp\n\u002F\u002F IEnumerable\u003CT> is covariant (out T):\nIEnumerable\u003Cstring> strings = new List\u003Cstring> { \"a\", \"b\" };\nIEnumerable\u003Cobject> objects = strings;  \u002F\u002F valid — covariance!\n\u002F\u002F A string IS-A object, so reading strings as objects is safe\n\n\u002F\u002F IReadOnlyList\u003CT> is covariant:\nIReadOnlyList\u003Cstring> strList = new List\u003Cstring> { \"x\" };\nIReadOnlyList\u003Cobject> objList = strList; \u002F\u002F valid\n\n\u002F\u002F Defining a covariant interface:\ninterface IProducer\u003Cout T>   \u002F\u002F 'out' = covariant\n{\n    T Produce();             \u002F\u002F T only appears in output position — safe\n    \u002F\u002F void Consume(T item); \u002F\u002F would break type safety — input position forbidden\n}\n```\n\nCovariance is safe because you are only **reading** values out of the container —\nthe container will always give you a `string`, and a `string` is always a valid\n`object`. Writing would be unsafe (you could insert a non-string into a\n`List\u003Cstring>` disguised as `List\u003Cobject>`).\n\n**Rule of thumb:** `out T` = covariance = the type flows **out** of the API\n(producer). Safe for read-only interfaces. Familiar examples: `IEnumerable\u003Cout T>`,\n`IReadOnlyList\u003Cout T>`, `Func\u003Cout TResult>`.\n",{"id":209,"difficulty":91,"q":210,"a":211},"contravariance","What is contravariance in C# generics?","**Contravariance** is the reverse: a generic with a **less** derived type argument\nis assignable to one with a **more** derived type argument. Expressed with the `in`\nkeyword — the type parameter only appears in input (consumer) positions.\n\n```csharp\n\u002F\u002F Action\u003CT> is contravariant (in T):\nAction\u003Cobject> printObject = obj => Console.WriteLine(obj);\nAction\u003Cstring> printString = printObject;  \u002F\u002F valid — contravariance!\n\u002F\u002F If you can handle any object, you can certainly handle a string\n\nprintString(\"hello\"); \u002F\u002F works — string is passed to printObject handler\n\n\u002F\u002F IComparer\u003CT> is contravariant:\nIComparer\u003Cobject> objComparer = Comparer\u003Cobject>.Default;\nIComparer\u003Cstring> strComparer = objComparer; \u002F\u002F valid\n\n\u002F\u002F Defining a contravariant interface:\ninterface IConsumer\u003Cin T>   \u002F\u002F 'in' = contravariant\n{\n    void Consume(T item);   \u002F\u002F T only appears in input position — safe\n    \u002F\u002F T Produce();         \u002F\u002F would break type safety — output position forbidden\n}\n```\n\nThe logic: if a method can handle any `object`, it can certainly handle a `string`.\nContravariance is about **consuming** values — a handler for the base type is\nalways compatible with a handler for the derived type.\n\n**Rule of thumb:** `in T` = contravariance = the type flows **into** the API\n(consumer). Familiar examples: `Action\u003Cin T>`, `IComparer\u003Cin T>`,\n`IEqualityComparer\u003Cin T>`.\n",{"id":213,"difficulty":34,"q":214,"a":215},"ienumerable-vs-icollection","What is the difference between `IEnumerable\u003CT>` and `ICollection\u003CT>` in generics?","`IEnumerable\u003CT>` is the minimal read\u002Fiterate interface. `ICollection\u003CT>` extends\nit to add count and mutation operations. Choosing the right abstraction in method\nsignatures controls what callers can pass and what you can do with the argument.\n\n```csharp\n\u002F\u002F IEnumerable\u003CT> — iterate only, no count, no add\u002Fremove\nvoid PrintAll\u003CT>(IEnumerable\u003CT> items)\n{\n    foreach (var item in items) Console.WriteLine(item); \u002F\u002F fine\n    \u002F\u002F items.Count — not available\n    \u002F\u002F items.Add(...)  — not available\n}\n\n\u002F\u002F ICollection\u003CT> — adds Count, Add, Remove, Contains, Clear\nvoid AddRange\u003CT>(ICollection\u003CT> target, IEnumerable\u003CT> source)\n{\n    foreach (var item in source) target.Add(item); \u002F\u002F Add available\n    Console.WriteLine(target.Count);               \u002F\u002F Count available\n}\n\n\u002F\u002F IList\u003CT> extends ICollection\u003CT> — adds index access\nvoid SwapFirst\u003CT>(IList\u003CT> list) { (list[0], list[1]) = (list[1], list[0]); }\n```\n\nThe interface hierarchy: `IEnumerable\u003CT>` ← `ICollection\u003CT>` ← `IList\u003CT>`.\nUse the **narrowest** interface that satisfies your needs in method parameters —\nthis maximises what callers can pass (arrays, LINQ queries, custom enumerables).\n\n**Rule of thumb:** Accept `IEnumerable\u003CT>` if you only iterate, `ICollection\u003CT>`\nif you need `Count` or mutation, `IList\u003CT>` if you need index access.\nReturn concrete types (`List\u003CT>`) so callers have the full API.\n",{"id":217,"difficulty":34,"q":218,"a":219},"csharp-vs-java-generics","How do C# generics differ from Java generics?","C# generics are **reified** — the type parameter exists at runtime and each\nclosed generic type (e.g., `List\u003Cint>`, `List\u003Cstring>`) is a distinct CLR type.\nJava generics use **type erasure** — type parameters are removed at compile time\nand replaced with `Object` (or the bound type), leaving only one class at runtime.\n\n```csharp\n\u002F\u002F C# — type parameters survive to runtime:\nvar list = new List\u003Cint>();\nConsole.WriteLine(list.GetType().Name);           \u002F\u002F \"List`1\"\nConsole.WriteLine(list.GetType().GenericTypeArguments[0]); \u002F\u002F System.Int32\n\n\u002F\u002F C# — value type specialisation: List\u003Cint> uses int[] internally, no boxing\nvar ints = new List\u003Cint> { 1, 2, 3 }; \u002F\u002F zero boxing\n\n\u002F\u002F C# — can use typeof on generic params:\nvoid Print\u003CT>() => Console.WriteLine(typeof(T).Name);\nPrint\u003CDateTime>(); \u002F\u002F \"DateTime\"\n\n\u002F\u002F Java equivalent would erase T → Object at runtime\n\u002F\u002F Java: new ArrayList\u003CInteger>() has no Integer info at runtime\n\u002F\u002F Java: no specialised ArrayList\u003Cint> — autoboxing to Integer always required\n```\n\nConsequences of C# reification: `typeof(T)` works inside generics, `is T` checks\nare valid, value-type specialisations avoid boxing, and you get distinct JIT code\nper value-type argument. The cost is larger compiled output for many value-type\nspecialisations.\n\n**Rule of thumb:** C# generics are more powerful and more efficient for value\ntypes than Java generics. Use `typeof(T)` and `default(T)` freely in C# — these\nhave no Java equivalent without reflection hacks.\n",{"id":221,"difficulty":34,"q":222,"a":223},"default-t","What is `default(T)` and when would you use it?","`default(T)` returns the **default value** for type `T`: `0` for numeric types,\n`false` for `bool`, `null` for reference types, and zero-initialised for structs.\nIt is the only way to get a valid zero\u002Fnull for an unknown type parameter.\n\n```csharp\nT GetValueOrDefault\u003CT>(Dictionary\u003Cstring, T> dict, string key)\n{\n    return dict.TryGetValue(key, out T? value) ? value : default(T);\n    \u002F\u002F If T is int  → returns 0\n    \u002F\u002F If T is bool → returns false\n    \u002F\u002F If T is string → returns null\n}\n\n\u002F\u002F C# 7.1+: 'default' literal (type inferred from context)\nT[] CreateArray\u003CT>(int length)\n{\n    var arr = new T[length]; \u002F\u002F already zero\u002Fdefault initialised\n    return arr;\n}\n\n\u002F\u002F Useful in generic algorithms:\nT Min\u003CT>(T[] arr) where T : IComparable\u003CT>\n{\n    T min = default!; \u002F\u002F non-null assertion needed with nullable ref types\n    bool first = true;\n    foreach (var item in arr)\n    {\n        if (first || item.CompareTo(min) \u003C 0) { min = item; first = false; }\n    }\n    return min;\n}\n```\n\n**Rule of thumb:** Use `default(T)` or the `default` literal when you need a\n\"zero\" starting value for a type parameter — it is the generic equivalent of\nwriting `0`, `false`, or `null` for concrete types.\n",{"id":225,"difficulty":34,"q":226,"a":227},"generic-delegates","What are the built-in generic delegates `Action\u003CT>`, `Func\u003CT>`, and `Predicate\u003CT>`?","These BCL delegates cover the vast majority of generic callback needs without\nrequiring custom delegate declarations.\n\n```csharp\n\u002F\u002F Action\u003CT...> — takes up to 16 params, returns void\nAction\u003Cstring> print = s => Console.WriteLine(s);\nAction\u003Cint, int> add = (a, b) => Console.WriteLine(a + b);\nprint(\"hello\");   \u002F\u002F \"hello\"\nadd(2, 3);        \u002F\u002F 5\n\n\u002F\u002F Func\u003CT..., TResult> — takes up to 16 params, returns TResult\nFunc\u003Cint, int, int> multiply = (a, b) => a * b;\nFunc\u003Cstring, int>   parse    = int.Parse;\nConsole.WriteLine(multiply(3, 4)); \u002F\u002F 12\n\n\u002F\u002F Predicate\u003CT> — exactly Func\u003CT, bool>, used in List\u003CT>.Find etc.\nPredicate\u003Cint> isEven = n => n % 2 == 0;\nvar nums = new List\u003Cint> { 1, 2, 3, 4 };\nConsole.WriteLine(nums.FindIndex(isEven)); \u002F\u002F 1\n\n\u002F\u002F LINQ uses Func throughout — these three delegate types compose naturally:\nvar evens = nums.Where(isEven.Invoke).Select(n => n * 10).ToList();\n```\n\n`Predicate\u003CT>` is literally `Func\u003CT, bool>` — they are separate types only for\nhistorical API compatibility (`List\u003CT>` predates LINQ). You can convert with\n`.Invoke` or a lambda wrapper.\n\n**Rule of thumb:** Use `Func\u003C>` for functions, `Action\u003C>` for side effects, and\n`Predicate\u003CT>` only when an API specifically requires it. Never define a custom\nsingle-method delegate unless you need a more descriptive name for API clarity.\n",{"id":229,"difficulty":34,"q":230,"a":231},"generic-interface-multiple","Can a class implement the same generic interface multiple times with different type arguments?","Yes, a class can implement `IInterface\u003CT>` for multiple closed types, but only\nthrough the explicit interface implementation syntax when the method signatures\nwould conflict.\n\n```csharp\ninterface IConverter\u003CT>\n{\n    T Convert(string input);\n}\n\nclass MultiConverter : IConverter\u003Cint>, IConverter\u003Cdouble>\n{\n    \u002F\u002F Explicit implementations avoid member name collision:\n    int IConverter\u003Cint>.Convert(string input) => int.Parse(input);\n    double IConverter\u003Cdouble>.Convert(string input) => double.Parse(input);\n}\n\nvar mc = new MultiConverter();\nint i = ((IConverter\u003Cint>)mc).Convert(\"42\");\ndouble d = ((IConverter\u003Cdouble>)mc).Convert(\"3.14\");\nConsole.WriteLine($\"{i}, {d}\"); \u002F\u002F 42, 3.14\n\n\u002F\u002F Common real-world example — IEquatable\u003CT> and IComparable\u003CT>:\nstruct Temperature : IComparable\u003CTemperature>, IEquatable\u003CTemperature>\n{\n    public double Celsius { get; init; }\n    public int CompareTo(Temperature other) => Celsius.CompareTo(other.Celsius);\n    public bool Equals(Temperature other) => Celsius == other.Celsius;\n}\n```\n\n**Rule of thumb:** A class implementing the same interface for multiple type\nargs is valid but rare. It's most common in adapters or type-bridge scenarios.\nWhen signatures collide, use explicit interface implementation to disambiguate.\n",{"id":233,"difficulty":34,"q":234,"a":235},"typeof-vs-gettype","What is the difference between `typeof(T)` and `t.GetType()` inside a generic method?","`typeof(T)` is a **compile-time** operator that returns the `Type` object for the\nstatic type parameter `T`. `t.GetType()` is a **runtime** call on an instance `t`\nthat returns its actual runtime type — which may be a subclass of `T`.\n\n```csharp\nvoid Inspect\u003CT>(T value)\n{\n    Console.WriteLine(typeof(T).Name);   \u002F\u002F static type parameter\n    Console.WriteLine(value.GetType().Name); \u002F\u002F actual runtime type of the instance\n}\n\nInspect\u003Cobject>(\"hello\");\n\u002F\u002F typeof(T)      → \"Object\"  (T is declared as 'object')\n\u002F\u002F GetType()      → \"String\"  (the actual object is a string)\n\nInspect\u003Cstring>(\"hello\");\n\u002F\u002F typeof(T)      → \"String\"\n\u002F\u002F GetType()      → \"String\"   (same in this case)\n\n\u002F\u002F Use typeof(T) when you need the declared type:\nbool IsValueType\u003CT>() => typeof(T).IsValueType;\nConsole.WriteLine(IsValueType\u003Cint>());    \u002F\u002F True\nConsole.WriteLine(IsValueType\u003Cstring>()); \u002F\u002F False\n```\n\nIn most generic code you want `typeof(T)` — it is a constant, avoids a virtual\ndispatch, and works even when `value` is `null`. Use `GetType()` only when you\nspecifically need to know the **runtime subtype** of the instance.\n\n**Rule of thumb:** `typeof(T)` = the type the author declared. `GetType()` = the\ntype the caller actually passed. They differ when `T` is a base class or interface\nand the instance is a derived type.\n",{"id":237,"difficulty":42,"q":238,"a":239},"generic-list-vs-arraylist","What is the difference between `List\u003CT>` and `ArrayList`, and why should you always prefer `List\u003CT>`?","`ArrayList` stores items as `object` — no type safety, every value type is boxed.\n`List\u003CT>` is generic — type-safe, no boxing, and produces helpful compile-time\nerrors rather than runtime `InvalidCastException`.\n\n```csharp\n\u002F\u002F ArrayList — pre-.NET 2 approach, avoid in new code\nvar old = new ArrayList();\nold.Add(42);        \u002F\u002F boxes int → object (heap allocation)\nold.Add(\"oops\");    \u002F\u002F compiles fine — runtime disaster waiting\nint x = (int)old[1]; \u002F\u002F InvalidCastException at runtime!\n\n\u002F\u002F List\u003CT> — correct approach\nvar list = new List\u003Cint>();\nlist.Add(42);       \u002F\u002F no boxing\n\u002F\u002F list.Add(\"oops\"); \u002F\u002F compile-time error — caught immediately\nint y = list[0];    \u002F\u002F no cast needed\nConsole.WriteLine(list.Count); \u002F\u002F 1\n```\n\n`ArrayList` still exists in .NET for legacy compatibility but is effectively\ndeprecated for new code. The only remaining use case is interoperating with old\nCOM APIs or reflection scenarios where the type is unknown at compile time\n(in which case `List\u003Cobject>` is still cleaner than `ArrayList`).\n\n**Rule of thumb:** Never use `ArrayList`, `Hashtable`, or other non-generic\ncollections in new C# code. Always use their generic equivalents: `List\u003CT>`,\n`Dictionary\u003CTKey, TValue>`, `HashSet\u003CT>`.\n",{"id":241,"difficulty":91,"q":242,"a":243},"generic-static-members","Do generic types share static members across different type arguments?","No. In C#, each **closed generic type** (`List\u003Cint>`, `List\u003Cstring>`) is a\ndistinct CLR type with its own set of static members. A static field on\n`MyClass\u003CT>` is **not shared** between `MyClass\u003Cint>` and `MyClass\u003Cstring>`.\n\n```csharp\nclass Counter\u003CT>\n{\n    public static int Count = 0;\n    public Counter() => Count++;\n}\n\n_ = new Counter\u003Cint>();\n_ = new Counter\u003Cint>();\n_ = new Counter\u003Cstring>();\n\nConsole.WriteLine(Counter\u003Cint>.Count);    \u002F\u002F 2 — own counter\nConsole.WriteLine(Counter\u003Cstring>.Count); \u002F\u002F 1 — separate counter\nConsole.WriteLine(Counter\u003Cdouble>.Count); \u002F\u002F 0 — never instantiated\n\n\u002F\u002F Contrast with a non-generic class — one shared static field:\nclass NonGenericCounter\n{\n    public static int Count = 0;\n    public NonGenericCounter() => Count++;\n}\n\u002F\u002F All instances share NonGenericCounter.Count\n```\n\nThis is a direct consequence of CLR **reification** — each generic instantiation\nis a separate type. It enables type-specific caches (e.g., `EqualityComparer\u003CT>.Default`\ncaches one comparer per `T`) but can be a source of bugs when developers expect\nsharing.\n\n**Rule of thumb:** Never expect a static field on a generic type to be shared\nacross type arguments. If you need global shared state, put it in a non-generic\nouter class or a static class.\n",{"id":245,"difficulty":34,"q":246,"a":247},"open-vs-closed-generic","What is the difference between an open generic type and a closed generic type?","An **open generic type** still has unbound type parameters (e.g., `List\u003CT>`). A\n**closed generic type** has all type parameters substituted with concrete types\n(e.g., `List\u003Cint>`). You can only create instances of closed generic types.\n\n```csharp\n\u002F\u002F Closed type — concrete, instantiable:\nvar list = new List\u003Cint>(); \u002F\u002F List\u003Cint> is a closed type\n\n\u002F\u002F Open type — abstract, used with typeof for reflection:\nType open   = typeof(List\u003C>);          \u002F\u002F open generic — note the \u003C>\nType closed = typeof(List\u003Cint>);       \u002F\u002F closed generic\n\nConsole.WriteLine(open.IsGenericTypeDefinition);  \u002F\u002F True\nConsole.WriteLine(closed.IsGenericTypeDefinition); \u002F\u002F False\nConsole.WriteLine(closed.IsConstructedGenericType); \u002F\u002F True\n\n\u002F\u002F Construct a closed type from an open one at runtime:\nType constructed = open.MakeGenericType(typeof(string));\nobject instance  = Activator.CreateInstance(constructed)!; \u002F\u002F List\u003Cstring>\n\n\u002F\u002F Useful for generic plugin\u002Ffactory systems:\nvar repoType    = typeof(Repository\u003C>).MakeGenericType(entityType);\nvar repoInstance = Activator.CreateInstance(repoType);\n```\n\nOpen generic types appear in reflection-based DI containers and ORMs. For example,\nregistering `typeof(IRepository\u003C>)` → `typeof(Repository\u003C>)` lets the container\nresolve `IRepository\u003CUser>` as `Repository\u003CUser>` automatically.\n\n**Rule of thumb:** You work with open generic types mostly in DI registration and\nreflection. In application code, you always deal with closed types. Use\n`MakeGenericType` when you need to construct a generic type from a runtime\n`Type` object.\n",{"id":249,"difficulty":91,"q":250,"a":251},"generic-math-interfaces","What are generic math interfaces in .NET 7+ and how do they enable numeric generics?",".NET 7 introduced a set of interfaces in `System.Numerics` — `INumber\u003CT>`,\n`IAdditionOperators\u003CTSelf, TOther, TResult>`, `IComparable\u003CT>`, etc. — that allow\nyou to write generic algorithms over numeric types without reflection or casting.\n\n```csharp\nusing System.Numerics;\n\n\u002F\u002F Before .NET 7: had to duplicate Sum\u003Cint>, Sum\u003Cdouble>, Sum\u003Cdecimal> ...\n\u002F\u002F .NET 7+: one generic method works for all numeric types\nT Sum\u003CT>(IEnumerable\u003CT> source) where T : INumber\u003CT>\n{\n    T total = T.Zero;                  \u002F\u002F INumber\u003CT> provides Zero\n    foreach (T item in source)\n        total += item;                 \u002F\u002F += via IAdditionOperators\n    return total;\n}\n\nConsole.WriteLine(Sum(new[] { 1, 2, 3 }));          \u002F\u002F 6    (int)\nConsole.WriteLine(Sum(new[] { 1.1, 2.2, 3.3 }));    \u002F\u002F 6.6  (double)\nConsole.WriteLine(Sum(new[] { 1m, 2m, 3m }));       \u002F\u002F 6    (decimal)\n\n\u002F\u002F Parse any number type generically:\nT Parse\u003CT>(string s) where T : IParsable\u003CT>\n    => T.Parse(s, null);\n\nint i = Parse\u003Cint>(\"42\");\ndouble d = Parse\u003Cdouble>(\"3.14\");\n```\n\nThis capability, called **static abstract interface members** (C# 11), allows\ninterfaces to declare static operators and factory methods that must be implemented\nby conforming types.\n\n**Rule of thumb:** Use `INumber\u003CT>` or the narrower arithmetic interfaces\n(`IAdditionOperators`, `IMultiplyOperators`) when writing library-level numeric\nalgorithms. For application code, concrete types are clearer and simpler.\n",{"id":253,"difficulty":34,"q":254,"a":255},"generic-type-inference","How does C# infer generic type arguments, and when do you have to specify them explicitly?","The C# compiler infers generic type arguments from the **types of the method\narguments** passed at the call site. Inference works only for method type parameters\n— class type parameters always require explicit specification.\n\n```csharp\n\u002F\u002F Compiler infers T from the argument type:\nT Identity\u003CT>(T value) => value;\n\nvar s = Identity(\"hello\");  \u002F\u002F T inferred as string\nvar n = Identity(42);       \u002F\u002F T inferred as int\n\u002F\u002F No need to write Identity\u003Cstring>(\"hello\")\n\n\u002F\u002F Inference fails when T appears only in the return type:\nT Create\u003CT>() where T : new() => new T();\n\u002F\u002F var x = Create();        \u002F\u002F error — cannot infer T from no arguments\nvar x = Create\u003CStringBuilder>(); \u002F\u002F must specify explicitly\n\n\u002F\u002F Partial inference is not allowed — specify all or none:\nvoid Combine\u003CT1, T2>(T1 a, T2 b) { }\nCombine(1, \"hi\");           \u002F\u002F both inferred: T1=int, T2=string\n\u002F\u002F Combine\u003Cint>(\"hi\");      \u002F\u002F compile error — cannot partially specify\n\n\u002F\u002F Return-type inference for delegates\u002Flambdas (C# 10):\nvar square = (int x) => x * x; \u002F\u002F inferred as Func\u003Cint, int>\n```\n\nType inference uses **unification** — it matches each argument's type against the\ncorresponding parameter's declared type to deduce `T`. When multiple arguments\nconstrain the same `T` to different types and neither is convertible to the other,\ninference fails and you must specify explicitly.\n\n**Rule of thumb:** Let the compiler infer when arguments supply all the type\ninformation needed. Specify explicitly when `T` is in the return type only, when\ninference would choose the wrong type, or when you need to override the inferred type\nfor clarity.\n",{"description":31},"C# generics interview questions — type constraints, covariance and contravariance, reification, generic delegates, and performance implications.","dotnet\u002Ffundamentals\u002Fgenerics","1GOXwCbAA_czF6emJ43fMJtMHHM9dL-ajJ1eKt7-GmQ",{"id":261,"title":262,"body":263,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":267,"navigation":37,"order":268,"path":269,"questions":270,"questionsCount":102,"related":103,"seo":331,"seoDescription":332,"stem":333,"subtopic":334,"topic":19,"topicSlug":20,"updated":108,"__hash__":335},"qa\u002Fdotnet\u002Ffundamentals\u002Flinq.md","Linq",{"type":28,"value":264,"toc":265},[],{"title":31,"searchDepth":32,"depth":32,"links":266},[],{},4,"\u002Fdotnet\u002Ffundamentals\u002Flinq",[271,275,279,283,287,291,295,299,303,307,311,315,319,323,327],{"id":272,"difficulty":42,"q":273,"a":274},"what-is-linq","What is LINQ and what problem does it solve?","**Language Integrated Query (LINQ)** is a set of C# language features and BCL\nextension methods that let you query any data source — collections, XML, databases,\nJSON — using a uniform, type-safe syntax directly in C# code.\n\n```csharp\nint[] numbers = { 5, 3, 8, 1, 9, 2 };\n\n\u002F\u002F Without LINQ: imperative loop\nvar evens = new List\u003Cint>();\nforeach (var n in numbers)\n    if (n % 2 == 0) evens.Add(n);\n\n\u002F\u002F With LINQ method syntax:\nvar evensLinq = numbers.Where(n => n % 2 == 0)\n                       .OrderBy(n => n)\n                       .ToList();  \u002F\u002F [2, 8]\n\n\u002F\u002F Query syntax (translates to the same method calls):\nvar evensQuery = (from n in numbers\n                  where n % 2 == 0\n                  orderby n\n                  select n).ToList();\n```\n\nLINQ is not limited to in-memory collections. The same syntax works for SQL\ndatabases via Entity Framework (`IQueryable\u003CT>`), XML via LINQ to XML, and any\ncustom data source that implements `IEnumerable\u003CT>` or `IQueryable\u003CT>`.\n\n**Rule of thumb:** LINQ makes collection operations declarative and composable —\nyou describe *what* you want, not *how* to iterate. Prefer it over imperative\nloops for readability; fall back to `foreach` only when performance profiling\njustifies it.\n",{"id":276,"difficulty":34,"q":277,"a":278},"deferred-execution","What is deferred execution in LINQ and why does it matter?","**Deferred execution** means a LINQ query is not evaluated when it is defined —\nit is evaluated only when you iterate the result. Each iteration re-runs the query\nagainst the current state of the source.\n\n```csharp\nvar numbers = new List\u003Cint> { 1, 2, 3, 4, 5 };\n\n\u002F\u002F This builds a query object — no iteration yet:\nvar query = numbers.Where(n => n > 2); \u002F\u002F deferred\n\nnumbers.Add(10); \u002F\u002F modify source after query is defined\n\nforeach (var n in query) Console.Write(n + \" \"); \u002F\u002F 3 4 5 10 — sees the new item!\n\n\u002F\u002F Force immediate execution with ToList() \u002F ToArray() \u002F Count():\nvar snapshot = numbers.Where(n => n > 2).ToList(); \u002F\u002F evaluated NOW\nnumbers.Add(99);\n\u002F\u002F 'snapshot' still contains { 3, 4, 5, 10 } — not affected by later changes\n```\n\nDeferred execution enables **query composition** — you can build a query\nincrementally and it only hits the data source once when iterated. The danger is\n**multiple enumeration**: iterating a deferred query twice runs it twice. Pass\na `IEnumerable\u003CT>` to a method that iterates it twice and you pay double the cost.\n\n**Rule of thumb:** Call `.ToList()` or `.ToArray()` to materialise when you will\niterate multiple times, need a point-in-time snapshot, or want to avoid re-querying\na database in EF Core.\n",{"id":280,"difficulty":42,"q":281,"a":282},"query-vs-method-syntax","What is the difference between LINQ query syntax and method syntax?","Both syntaxes produce identical IL — the compiler translates query syntax into\nmethod calls. Query syntax is SQL-like; method syntax (fluent) uses extension\nmethods chained with lambdas.\n\n```csharp\nvar data = new[] { 1, 2, 3, 4, 5, 6 };\n\n\u002F\u002F Query syntax:\nvar q1 = from n in data\n         where n % 2 == 0\n         orderby n descending\n         select n * n;\n\n\u002F\u002F Method (fluent) syntax — identical result:\nvar q2 = data.Where(n => n % 2 == 0)\n             .OrderByDescending(n => n)\n             .Select(n => n * n);\n\n\u002F\u002F Some operators only exist in method syntax (no query-syntax equivalent):\nvar q3 = data.Zip(data.Reverse(), (a, b) => a + b); \u002F\u002F Zip has no query syntax\nvar q4 = data.Aggregate((acc, n) => acc + n);        \u002F\u002F Aggregate has no query syntax\nvar count = data.Count(n => n > 3);                  \u002F\u002F Count with predicate — method only\n```\n\n**Method syntax** is more common in practice because it composes naturally with\nmethod chaining and covers more operators. **Query syntax** reads more naturally\nfor complex multi-source joins, let clauses, and group-by queries.\n\n**Rule of thumb:** Default to method syntax — it covers every operator and is\nwhat most .NET codebases use. Switch to query syntax for complex joins or when\nthe `let` keyword (intermediate variable) would significantly aid readability.\n",{"id":284,"difficulty":34,"q":285,"a":286},"ienumerable-vs-iqueryable","What is the difference between `IEnumerable\u003CT>` and `IQueryable\u003CT>` in LINQ?","`IEnumerable\u003CT>` executes LINQ in **memory** (LINQ to Objects). `IQueryable\u003CT>`\n**translates** the LINQ expression tree into the query language of the data source\n(typically SQL) and executes it there.\n\n```csharp\n\u002F\u002F IEnumerable: filtering happens in C# after loading ALL rows from DB\nIEnumerable\u003CUser> usersEnum = dbContext.Users.ToList(); \u002F\u002F loads ALL users first\nvar active = usersEnum.Where(u => u.IsActive); \u002F\u002F filters in memory — bad!\n\n\u002F\u002F IQueryable: filtering is translated to SQL — only matching rows are fetched\nIQueryable\u003CUser> usersQuery = dbContext.Users;  \u002F\u002F no DB hit yet\nvar activeQ = usersQuery.Where(u => u.IsActive); \u002F\u002F adds WHERE to SQL expression\nvar result = activeQ.ToList(); \u002F\u002F ONE SQL: SELECT * FROM Users WHERE IsActive = 1\n```\n\n`IQueryable\u003CT>` stores an **expression tree** — a data structure representing the\nquery. When you call `ToList()`, the LINQ provider (EF Core, LINQ to SQL) walks\nthe expression tree and generates the appropriate query. `IEnumerable\u003CT>` just\nholds a delegate — it has no visibility into what the lambda does and cannot\ntranslate it.\n\n**Rule of thumb:** Use `IQueryable\u003CT>` for database queries so filtering, sorting,\nand pagination happen in SQL. Convert to `IEnumerable\u003CT>` (`.AsEnumerable()`) only\nwhen you need to run logic the provider cannot translate.\n",{"id":288,"difficulty":34,"q":289,"a":290},"select-vs-selectmany","What is the difference between `Select` and `SelectMany` in LINQ?","`Select` projects each element to one output element (1-to-1). `SelectMany`\nprojects each element to a **sequence** and flattens all those sequences into a\nsingle output sequence (1-to-many → flat).\n\n```csharp\nvar orders = new[]\n{\n    new { Id = 1, Items = new[] { \"pen\", \"book\" } },\n    new { Id = 2, Items = new[] { \"desk\", \"chair\", \"lamp\" } },\n};\n\n\u002F\u002F Select — returns IEnumerable\u003Cstring[]>: each order maps to its array\nvar nested = orders.Select(o => o.Items);\n\u002F\u002F Result: [ [\"pen\",\"book\"], [\"desk\",\"chair\",\"lamp\"] ]\n\n\u002F\u002F SelectMany — flattens into IEnumerable\u003Cstring>\nvar flat = orders.SelectMany(o => o.Items);\n\u002F\u002F Result: [\"pen\", \"book\", \"desk\", \"chair\", \"lamp\"]\n\n\u002F\u002F Common use: get all characters in all words:\nvar words = new[] { \"hello\", \"world\" };\nvar chars = words.SelectMany(w => w.ToCharArray()).Distinct().OrderBy(c => c);\n\u002F\u002F d e h l o r w\n```\n\n`SelectMany` is equivalent to a nested `foreach` that adds each inner element to a\nflat output list. In query syntax, it corresponds to multiple `from` clauses.\n\n**Rule of thumb:** If each source element maps to a collection and you want a\nflat result, use `SelectMany`. If you want a sequence of sequences (or one-to-one),\nuse `Select`.\n",{"id":292,"difficulty":42,"q":293,"a":294},"first-single-default","What is the difference between `First()`, `FirstOrDefault()`, `Single()`, and `SingleOrDefault()`?","These four methods differ on **how many elements are expected** and **what happens\nwhen the expectation is violated**.\n\n```csharp\nvar empty = Array.Empty\u003Cint>();\nvar one   = new[] { 42 };\nvar multi = new[] { 1, 2, 3 };\n\n\u002F\u002F First() — expects at least one; throws if empty\nmulti.First();            \u002F\u002F 1\nmulti.First(n => n > 1);  \u002F\u002F 2\n\u002F\u002F empty.First();         \u002F\u002F InvalidOperationException!\n\n\u002F\u002F FirstOrDefault() — returns default(T) if empty, no throw\nempty.FirstOrDefault();   \u002F\u002F 0 (default int)\nempty.FirstOrDefault(-1); \u002F\u002F -1 (C# 10 default value param)\n\n\u002F\u002F Single() — expects exactly one; throws if empty OR if more than one\none.Single();             \u002F\u002F 42\n\u002F\u002F multi.Single();        \u002F\u002F InvalidOperationException — more than one element!\n\u002F\u002F empty.Single();        \u002F\u002F InvalidOperationException — sequence is empty!\n\n\u002F\u002F SingleOrDefault() — returns default if empty; throws if more than one\none.SingleOrDefault();    \u002F\u002F 42\nempty.SingleOrDefault();  \u002F\u002F 0 — fine\n\u002F\u002F multi.SingleOrDefault(); \u002F\u002F InvalidOperationException — still throws for multiple!\n```\n\n**Rule of thumb:** Use `First`\u002F`FirstOrDefault` when you expect a list and want the\nfirst match. Use `Single`\u002F`SingleOrDefault` when exactly one result is a business\nrequirement (e.g., look up by unique ID) — the exception is a useful assertion.\n",{"id":296,"difficulty":34,"q":297,"a":298},"groupby","How does `GroupBy` work in LINQ?","`GroupBy` partitions elements into groups based on a key. It returns\n`IEnumerable\u003CIGrouping\u003CTKey, TElement>>` — each group has a `Key` property and\nis itself an `IEnumerable\u003CTElement>`.\n\n```csharp\nvar products = new[]\n{\n    new { Name = \"Pen\",   Category = \"Stationery\", Price = 1.5  },\n    new { Name = \"Book\",  Category = \"Education\",  Price = 12.0 },\n    new { Name = \"Ruler\", Category = \"Stationery\", Price = 0.8  },\n    new { Name = \"Calc\",  Category = \"Education\",  Price = 25.0 },\n};\n\nvar byCategory = products.GroupBy(p => p.Category);\n\nforeach (var group in byCategory)\n{\n    Console.WriteLine($\"{group.Key}: {group.Count()} items, \" +\n                      $\"avg £{group.Average(p => p.Price):F2}\");\n}\n\u002F\u002F Stationery: 2 items, avg £1.15\n\u002F\u002F Education:  2 items, avg £18.50\n\n\u002F\u002F With element projection:\nvar namesByCategory = products\n    .GroupBy(p => p.Category, p => p.Name)\n    .ToDictionary(g => g.Key, g => g.ToList());\n\u002F\u002F { \"Stationery\": [\"Pen\",\"Ruler\"], \"Education\": [\"Book\",\"Calc\"] }\n```\n\nIn EF Core, `GroupBy` on `IQueryable\u003CT>` translates to SQL `GROUP BY`. However,\nnot all LINQ `GroupBy` projections can be translated — EF Core will throw at runtime\nif it cannot. Use `.AsEnumerable()` before `GroupBy` to force in-memory grouping\nwhen needed.\n\n**Rule of thumb:** `GroupBy` is for aggregating by a category. Always follow it\nwith `Count()`, `Sum()`, `Average()`, `Max()`, or a materialising call — iterating\nan `IGrouping` multiple times re-evaluates the source.\n",{"id":300,"difficulty":42,"q":301,"a":302},"any-all-count","When do you use `Any()`, `All()`, and `Count()` in LINQ?","These three check membership or quantity. Knowing which to use avoids common\nperformance mistakes.\n\n```csharp\nvar nums = new[] { 1, 2, 3, 4, 5 };\n\n\u002F\u002F Any() — \"does at least one element satisfy the condition?\"\nnums.Any();              \u002F\u002F true (sequence is non-empty)\nnums.Any(n => n > 10);  \u002F\u002F false\n\n\u002F\u002F All() — \"do ALL elements satisfy the condition?\"\nnums.All(n => n > 0);   \u002F\u002F true\nnums.All(n => n > 3);   \u002F\u002F false\n\n\u002F\u002F Count() — how many satisfy the condition?\nnums.Count();            \u002F\u002F 5\nnums.Count(n => n > 3); \u002F\u002F 2 (4 and 5)\n\n\u002F\u002F COMMON MISTAKE — avoid:\nif (list.Count() > 0) { }   \u002F\u002F iterates entire list to count\n\u002F\u002F Prefer:\nif (list.Any()) { }         \u002F\u002F stops at first element — O(1) for List\u003CT>\n```\n\nFor `ICollection\u003CT>` (like `List\u003CT>`), `.Count` (property, not method) is O(1).\nThe `Count()` extension method iterates when the source is not an `ICollection\u003CT>`.\nIn EF Core, both translate to `SELECT COUNT(*)` in SQL, so the difference is moot\nthere — but `Any()` translates to `EXISTS (SELECT 1 ...)` which can be faster.\n\n**Rule of thumb:** Use `Any()` to check emptiness or existence. Use `Count()` only\nwhen you need the actual number. Never call `Count() > 0` — use `Any()` instead.\n",{"id":304,"difficulty":34,"q":305,"a":306},"aggregate","What is `Aggregate()` in LINQ and what is it equivalent to in functional programming?","`Aggregate()` is LINQ's **fold** (also called `reduce`). It accumulates a sequence\ninto a single value by repeatedly applying a function to a running accumulator and\nthe next element.\n\n```csharp\nvar nums = new[] { 1, 2, 3, 4, 5 };\n\n\u002F\u002F Sum via Aggregate (seed defaults to first element):\nint sum = nums.Aggregate((acc, n) => acc + n); \u002F\u002F 15\n\n\u002F\u002F With explicit seed:\nint product = nums.Aggregate(1, (acc, n) => acc * n); \u002F\u002F 120\n\n\u002F\u002F With result selector (seed, fold, then transform):\nstring csv = nums.Aggregate(\n    new System.Text.StringBuilder(),\n    (sb, n) => { sb.Append(n).Append(','); return sb; },\n    sb => sb.ToString().TrimEnd(',')\n); \u002F\u002F \"1,2,3,4,5\"\n\n\u002F\u002F String joining (Aggregate style vs string.Join):\nstring joined = new[] { \"a\", \"b\", \"c\" }.Aggregate((a, b) => $\"{a},{b}\"); \u002F\u002F \"a,b,c\"\n\u002F\u002F Prefer: string.Join(\",\", new[] { \"a\",\"b\",\"c\" }) — more readable + efficient\n```\n\n`Aggregate` is the most general LINQ operator — `Sum`, `Max`, `Min`, `Count`, and\n`Average` are all special cases of `Aggregate`. Use the specialised versions when\nthey exist; reach for `Aggregate` only when none of the purpose-built operators fit.\n\n**Rule of thumb:** `Aggregate` = fold\u002Freduce. Use named operators (`Sum`, `Max`)\nwhen they exist. Use `Aggregate` for custom fold logic like building a running\nproduct, combining strings with separators, or computing complex rolling statistics.\n",{"id":308,"difficulty":34,"q":309,"a":310},"tolist-vs-asenumerable","What is the difference between `ToList()`, `ToArray()`, and `AsEnumerable()`?","`ToList()` and `ToArray()` both **materialise** a LINQ query into a concrete\nin-memory collection, forcing immediate execution. `AsEnumerable()` keeps the\nquery deferred but switches the compile-time type from `IQueryable\u003CT>` to\n`IEnumerable\u003CT>`, causing subsequent operators to run in memory.\n\n```csharp\nIQueryable\u003CUser> query = dbContext.Users.Where(u => u.IsActive);\n\n\u002F\u002F ToList() — executes SQL NOW, returns List\u003CUser>\nList\u003CUser> list = query.ToList();\n\n\u002F\u002F ToArray() — executes SQL NOW, returns User[]\nUser[] array = query.ToArray();\n\n\u002F\u002F AsEnumerable() — no execution yet; shifts to LINQ-to-Objects for subsequent ops\nIEnumerable\u003CUser> enumerable = query.AsEnumerable();\n\u002F\u002F The next Where runs in C# memory, not SQL:\nvar localFilter = enumerable.Where(u => MyComplexCSharpMethod(u));\n\u002F\u002F When you iterate 'localFilter', it fetches ALL active users from DB first, then filters\n```\n\nUse `AsEnumerable()` when a LINQ provider (EF Core) cannot translate a particular\noperator or lambda to SQL — it forces the remainder of the pipeline to run in\nmemory on the already-fetched rows.\n\n**Rule of thumb:** `ToList()` or `ToArray()` = execute now + store. `AsEnumerable()`\n= switch to in-memory LINQ without executing yet (the DB query still fires when you\niterate). Choose `ToList()` for most cases; `ToArray()` when you need a fixed-size buffer.\n",{"id":312,"difficulty":91,"q":313,"a":314},"linq-join","How does a LINQ join work, and what is the difference between `Join` and `GroupJoin`?","`Join` produces a flat sequence where each left element is matched with each right\nelement on a key — equivalent to SQL `INNER JOIN`. `GroupJoin` produces a\nhierarchical result — each left element paired with all its matching right elements\n— equivalent to SQL `LEFT OUTER JOIN` (when combined with `DefaultIfEmpty`).\n\n```csharp\nvar customers = new[] {\n    new { Id = 1, Name = \"Alice\" },\n    new { Id = 2, Name = \"Bob\"   },\n};\nvar orders = new[] {\n    new { CustomerId = 1, Item = \"Book\"  },\n    new { CustomerId = 1, Item = \"Pen\"   },\n    new { CustomerId = 2, Item = \"Desk\"  },\n};\n\n\u002F\u002F Join — flat INNER JOIN:\nvar inner = customers.Join(orders,\n    c => c.Id,          \u002F\u002F outer key\n    o => o.CustomerId,  \u002F\u002F inner key\n    (c, o) => $\"{c.Name}: {o.Item}\");\n\u002F\u002F Alice: Book, Alice: Pen, Bob: Desk\n\n\u002F\u002F GroupJoin — hierarchical LEFT JOIN:\nvar grouped = customers.GroupJoin(orders,\n    c => c.Id,\n    o => o.CustomerId,\n    (c, orderGroup) => new { c.Name, Orders = orderGroup.ToList() });\n\u002F\u002F { Name=\"Alice\", Orders=[Book,Pen] }, { Name=\"Bob\", Orders=[Desk] }\n```\n\nIn EF Core on `IQueryable`, `Join` translates to SQL `INNER JOIN` and `GroupJoin`\ntranslates to `LEFT JOIN` (when `.SelectMany(..., DefaultIfEmpty())` is applied).\n\n**Rule of thumb:** Use `Join` for inner joins (only matching records). Use\n`GroupJoin` when you need a parent with a collection of children, or when you\nwant to preserve left-side records even if there are no matches.\n",{"id":316,"difficulty":34,"q":317,"a":318},"zip","What is `Zip()` in LINQ and when would you use it?","`Zip` merges two (or three, since .NET 6) sequences element-by-element, producing\none output element per pair. It stops when the shorter sequence is exhausted.\n\n```csharp\nvar names  = new[] { \"Alice\", \"Bob\", \"Charlie\" };\nvar scores = new[] { 92, 85, 78 };\n\n\u002F\u002F Two-sequence Zip with result selector:\nvar results = names.Zip(scores, (name, score) => $\"{name}: {score}\");\n\u002F\u002F [\"Alice: 92\", \"Bob: 85\", \"Charlie: 78\"]\n\n\u002F\u002F C# 6+: Zip returning tuples (no selector needed):\nvar pairs = names.Zip(scores); \u002F\u002F IEnumerable\u003C(string, int)>\nforeach (var (name, score) in pairs)\n    Console.WriteLine($\"{name} scored {score}\");\n\n\u002F\u002F .NET 6+ — three-way Zip:\nvar grades = new[] { 'A', 'B', 'C' };\nvar triples = names.Zip(scores, grades); \u002F\u002F IEnumerable\u003C(string, int, char)>\n\n\u002F\u002F Stops at the shorter sequence:\nnew[] { 1, 2, 3 }.Zip(new[] { 'a', 'b' }); \u002F\u002F only two pairs: (1,'a'), (2,'b')\n```\n\nCommon use cases: combining a list of keys with a list of values, pairing\nchronological data from two sources, or producing numbered output by zipping with\n`Enumerable.Range`.\n\n**Rule of thumb:** Use `Zip` when two sequences are positionally aligned and you\nwant to process them in lock-step. If the sequences can have different orderings,\na `Join` on a key is safer.\n",{"id":320,"difficulty":42,"q":321,"a":322},"distinct-and-distinctby","How do `Distinct()` and `DistinctBy()` work in LINQ?","`Distinct()` removes duplicate elements using equality comparison on the element\nitself. `DistinctBy()` (.NET 6+) removes duplicates based on a **key selector** —\nkeeping the first element with each distinct key value.\n\n```csharp\nvar nums = new[] { 1, 2, 2, 3, 3, 3 };\nvar unique = nums.Distinct(); \u002F\u002F { 1, 2, 3 }\n\n\u002F\u002F Custom equality for objects — requires IEqualityComparer\u003CT>:\nvar people = new[]\n{\n    new { Name = \"Alice\", Dept = \"Eng\" },\n    new { Name = \"Bob\",   Dept = \"HR\"  },\n    new { Name = \"Alice\", Dept = \"HR\"  }, \u002F\u002F same Name, different Dept\n};\n\n\u002F\u002F Distinct() on anonymous types uses value equality (all fields):\n\u002F\u002F Result: all three — Name+Dept combination is different for each\n\n\u002F\u002F DistinctBy() — deduplicate by a single key, keeping first occurrence:\nvar byName = people.DistinctBy(p => p.Name);\n\u002F\u002F { Alice\u002FEng, Bob\u002FHR } — second Alice dropped\nforeach (var p in byName)\n    Console.WriteLine($\"{p.Name} \u002F {p.Dept}\");\n\n\u002F\u002F Useful for deduplicating DB results by a business key:\nvar latestOrders = orders.DistinctBy(o => o.CustomerId);\n```\n\n`Distinct()` uses the default equality comparer (`EqualityComparer\u003CT>.Default`).\nFor custom comparison pass an `IEqualityComparer\u003CT>` as the second argument.\n`DistinctBy` is the LINQ equivalent of SQL `DISTINCT ON` (PostgreSQL) or a\n`GROUP BY key HAVING ROW_NUMBER() = 1` pattern.\n\n**Rule of thumb:** Use `Distinct()` to deduplicate primitives and value-equal types.\nUse `DistinctBy(x => x.Key)` to pick one representative element per key from a\ncollection of objects.\n",{"id":324,"difficulty":42,"q":325,"a":326},"orderby-vs-thenby","What is the difference between `OrderBy` and `ThenBy` in LINQ?","`OrderBy` \u002F `OrderByDescending` establishes the **primary sort key**. `ThenBy` \u002F\n`ThenByDescending` adds a **secondary (tie-breaking) sort** applied only when the\nprimary key values are equal.\n\n```csharp\nvar employees = new[]\n{\n    new { Name = \"Charlie\", Dept = \"Eng\",  Salary = 80_000 },\n    new { Name = \"Alice\",   Dept = \"HR\",   Salary = 60_000 },\n    new { Name = \"Bob\",     Dept = \"Eng\",  Salary = 90_000 },\n    new { Name = \"Diana\",   Dept = \"HR\",   Salary = 70_000 },\n};\n\n\u002F\u002F Sort by Dept, then by Name within each dept:\nvar sorted = employees\n    .OrderBy(e => e.Dept)\n    .ThenBy(e => e.Name);\n\u002F\u002F Eng\u002FBob, Eng\u002FCharlie, HR\u002FAlice, HR\u002FDiana\n\n\u002F\u002F Multi-level descending:\nvar byDeptThenSalaryDesc = employees\n    .OrderBy(e => e.Dept)\n    .ThenByDescending(e => e.Salary);\n\u002F\u002F Eng\u002FBob(90k), Eng\u002FCharlie(80k), HR\u002FDiana(70k), HR\u002FAlice(60k)\n\n\u002F\u002F Note: chaining OrderBy twice is WRONG — second OrderBy replaces the first:\n\u002F\u002F Bad: employees.OrderBy(e => e.Dept).OrderBy(e => e.Name) — only sorts by Name\n\u002F\u002F Good: .OrderBy(e => e.Dept).ThenBy(e => e.Name)\n```\n\nLINQ's `OrderBy` uses a **stable sort** — elements with equal keys preserve their\noriginal relative order. This makes `ThenBy` reliable and predictable.\n\n**Rule of thumb:** Always use `ThenBy` \u002F `ThenByDescending` for secondary sort\ncriteria, never chain a second `OrderBy`. A chained `OrderBy` silently discards the\nprevious ordering.\n",{"id":328,"difficulty":91,"q":329,"a":330},"linq-performance-pitfalls","What are the most common LINQ performance pitfalls and how do you avoid them?","LINQ's composability can hide significant performance costs. The most common pitfalls\nare multiple enumeration, N+1 queries in EF Core, and misuse of `Count()` vs `Any()`.\n\n```csharp\n\u002F\u002F Pitfall 1 — multiple enumeration: iterates the source twice\nIEnumerable\u003Cint> query = GetExpensiveSequence();\nif (query.Any())          \u002F\u002F first enumeration\n    Process(query.First()); \u002F\u002F second enumeration\n\u002F\u002F Fix: materialise once\nvar list = query.ToList();\nif (list.Count > 0) Process(list[0]);\n\n\u002F\u002F Pitfall 2 — N+1 query: one DB hit per order\nforeach (var order in dbContext.Orders.ToList())       \u002F\u002F 1 query\n    Console.WriteLine(order.Customer.Name);            \u002F\u002F N queries (lazy load)\n\u002F\u002F Fix: eager load with Include\nforeach (var order in dbContext.Orders.Include(o => o.Customer).ToList())\n    Console.WriteLine(order.Customer.Name); \u002F\u002F 1 query with JOIN\n\n\u002F\u002F Pitfall 3 — Count() on IEnumerable iterates the whole sequence\nif (GetItems().Count() > 0) { }   \u002F\u002F O(n)\n\u002F\u002F Fix:\nif (GetItems().Any()) { }         \u002F\u002F O(1) stops at first element\n\n\u002F\u002F Pitfall 4 — Where before Select on large datasets (in memory):\nvar result = items.Select(Transform).Where(x => x.IsValid);\n\u002F\u002F Bad: Transform() called on ALL items\n\u002F\u002F Good: filter first, then project\nvar result2 = items.Where(x => x.IsEligible).Select(Transform);\n```\n\n**Rule of thumb:** Materialise with `ToList()` when you iterate more than once.\nUse `Include()` in EF Core to avoid N+1. Use `Any()` for existence checks. Filter\nwith `Where()` before projecting with `Select()` to minimise work.\n",{"description":31},"LINQ interview questions — deferred execution, IQueryable vs IEnumerable, Select vs SelectMany, GroupBy, and common performance pitfalls.","dotnet\u002Ffundamentals\u002Flinq","LINQ","gJ7OJobzzvZenzfBBIxrrXSSgDwGsGOTF1yIDGLGhhQ",{"id":337,"title":338,"body":339,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":343,"navigation":37,"order":344,"path":345,"questions":346,"questionsCount":102,"related":103,"seo":407,"seoDescription":408,"stem":409,"subtopic":338,"topic":19,"topicSlug":20,"updated":108,"__hash__":410},"qa\u002Fdotnet\u002Ffundamentals\u002Fnullable-types.md","Nullable Types",{"type":28,"value":340,"toc":341},[],{"title":31,"searchDepth":32,"depth":32,"links":342},[],{},5,"\u002Fdotnet\u002Ffundamentals\u002Fnullable-types",[347,351,355,359,363,367,371,375,379,383,387,391,395,399,403],{"id":348,"difficulty":42,"q":349,"a":350},"nullable-value-types","What are nullable value types in C#?","A **nullable value type** (`T?`) is a wrapper around a value type that adds the\nability to represent `null` in addition to the normal range of values. It is\nsyntactic sugar for `Nullable\u003CT>`.\n\n```csharp\nint  normal   = 42;\n\u002F\u002F normal = null; \u002F\u002F compile error — int cannot be null\n\nint? nullable = 42;\nnullable = null; \u002F\u002F fine — Nullable\u003Cint> can hold null\n\n\u002F\u002F Check before using:\nif (nullable.HasValue)\n    Console.WriteLine(nullable.Value); \u002F\u002F 42\n\n\u002F\u002F Safe access with GetValueOrDefault:\nint safe = nullable.GetValueOrDefault(-1); \u002F\u002F -1 if null, otherwise the value\n\n\u002F\u002F Common use: database columns that allow NULL\nint? age = GetAgeFromDatabase(userId); \u002F\u002F returns null if not set\n```\n\n`Nullable\u003CT>` is a struct with two fields: `bool HasValue` and `T Value`. When\n`HasValue` is false, accessing `Value` throws `InvalidOperationException`. The\n`T?` syntax is shorthand the compiler expands to `Nullable\u003CT>` automatically.\n\n**Rule of thumb:** Use `int?`, `DateTime?`, `bool?` (etc.) wherever a value is\ngenuinely optional or may be absent — particularly for database columns, optional\nform fields, and API responses where the field may be omitted.\n",{"id":352,"difficulty":42,"q":353,"a":354},"nullable-t-vs-nullable-int","What is the difference between `int?` and `Nullable\u003Cint>` in C#?","There is **no difference** — they are identical. `int?` is compiler syntactic\nsugar that is expanded to `System.Nullable\u003Cint>` before compilation. At runtime,\nboth have exactly the same representation.\n\n```csharp\nint?              a = 5;\nNullable\u003Cint>     b = 5;\nSystem.Nullable\u003Cint> c = 5;\n\n\u002F\u002F All three are the same type:\nConsole.WriteLine(a.GetType() == b.GetType()); \u002F\u002F True\nConsole.WriteLine(typeof(int?) == typeof(Nullable\u003Cint>)); \u002F\u002F True\n\n\u002F\u002F The ? syntax works for any non-nullable value type:\ndouble?   d = null;\nDateTime? dt = null;\nGuid?     g  = null;\nbool?     flag = null; \u002F\u002F three-state logic (true \u002F false \u002F unknown)\n```\n\nThe compiler accepts both forms everywhere — in variable declarations, method\nsignatures, and generic type arguments. `int?` is universally preferred in\nC# code for brevity.\n\n**Rule of thumb:** Always write `int?` not `Nullable\u003Cint>` — the short form is\nidiomatic C# and is what every IDE and style guide recommends.\n",{"id":356,"difficulty":42,"q":357,"a":358},"null-coalescing-operator","What is the null-coalescing operator `??` and when do you use it?","`??` returns the left operand if it is **not null**, otherwise returns the right\noperand. It is a concise way to provide a default value for a potentially null\nexpression.\n\n```csharp\nstring? name = GetName(); \u002F\u002F may return null\n\n\u002F\u002F Without ??: verbose conditional\nstring display = name != null ? name : \"Unknown\";\n\n\u002F\u002F With ??:\nstring display = name ?? \"Unknown\";\n\n\u002F\u002F Chains: returns first non-null value\nstring result = GetFromCache() ?? GetFromDatabase() ?? \"default\";\n\n\u002F\u002F Works with nullable value types:\nint? count = GetCount();\nint  safe  = count ?? 0;\n\n\u002F\u002F Nested in property initialisation:\npublic string Label => _label ?? (_label = ComputeLabel());\n```\n\n`??` short-circuits — if the left side is non-null, the right side is not\nevaluated. This makes it safe to use with method calls on the right side that\nhave side effects or are expensive.\n\n**Rule of thumb:** Replace `x != null ? x : y` with `x ?? y`. Chain `??` for\nfallback hierarchies (cache → database → hardcoded default).\n",{"id":360,"difficulty":42,"q":361,"a":362},"null-conditional-operator","What is the null-conditional operator `?.` and how does it prevent NullReferenceException?","`?.` (the null-conditional or safe-navigation operator) evaluates the left side and,\nif it is `null`, returns `null` instead of throwing `NullReferenceException`. It\nshort-circuits the entire chain on the first null.\n\n```csharp\nUser? user = GetUser(id); \u002F\u002F may return null\n\n\u002F\u002F Without ?.: manual null checks\nstring? city = null;\nif (user != null && user.Address != null)\n    city = user.Address.City;\n\n\u002F\u002F With ?.: concise null-safe chain\nstring? city = user?.Address?.City; \u002F\u002F null if user or Address is null\n\n\u002F\u002F With collections:\nint? count = user?.Orders?.Count; \u002F\u002F null if user or Orders is null\n\n\u002F\u002F Invoke a delegate safely:\nOnChanged?.Invoke(this, EventArgs.Empty); \u002F\u002F skip if delegate is null\n\n\u002F\u002F Combine with ??: provide a fallback\nstring display = user?.Name ?? \"Guest\";\n```\n\n`?.` returns `null` (or `default(T)` for value types, wrapped in `T?`) if any\nstep in the chain is null. The `?[]` form works the same way for indexers:\n`user?.Orders?[0]?.Item`.\n\n**Rule of thumb:** Use `?.` instead of nested null checks when navigating\nobject graphs. Combine with `??` to provide defaults:\n`user?.Profile?.Bio ?? \"No bio yet\"`.\n",{"id":364,"difficulty":34,"q":365,"a":366},"nullable-reference-types","What are nullable reference types introduced in C# 8 and how do they differ from nullable value types?","**Nullable reference types (NRT)** is a compile-time opt-in analysis feature\n(C# 8+) that lets you annotate reference types as nullable (`string?`) or\nnon-nullable (`string`). The CLR behaviour is unchanged — it is purely a\nstatic analysis layer.\n\n```csharp\n#nullable enable\n\nstring  nonNullable = \"hello\";  \u002F\u002F must never be null — compiler warns if you assign null\nstring? nullable    = null;     \u002F\u002F can be null — compiler warns if you dereference without check\n\n\u002F\u002F Compiler warns when you use a nullable without null check:\nConsole.WriteLine(nullable.Length); \u002F\u002F CS8602: Dereference of a possibly null reference.\n\n\u002F\u002F Safe pattern — use ? or check:\nConsole.WriteLine(nullable?.Length);\nif (nullable != null)\n    Console.WriteLine(nullable.Length); \u002F\u002F fine — flow analysis narrows to non-null\n\n\u002F\u002F Method signatures communicate intent:\nstring GetDisplayName(string? name) => name ?? \"Guest\";\n```\n\nUnlike `int?` (a real runtime Nullable\u003CT>), `string?` at runtime is just `string`\n— the `?` annotation exists only in metadata for the compiler. Enabling NRT in an\nexisting codebase typically surfaces many latent null-related bugs.\n\n**Rule of thumb:** Enable `\u003CNullable>enable\u003C\u002FNullable>` in new projects from day\none. On existing codebases, enable it per-file with `#nullable enable` and fix\nwarnings incrementally. Treat all warnings as errors once the codebase is clean.\n",{"id":368,"difficulty":34,"q":369,"a":370},"enable-nullable-reference-types","How do you enable nullable reference types for a .NET project?","Enable it project-wide in `.csproj`, or per-file with a pragma. Without enabling,\nall reference types are in the \"oblivious\" nullable state (no warnings either way).\n\n```xml\n\u003C!-- In .csproj — recommended for all new projects -->\n\u003CPropertyGroup>\n  \u003CNullable>enable\u003C\u002FNullable>\n  \u003CTreatWarningsAsErrors>true\u003C\u002FTreatWarningsAsErrors>  \u003C!-- optional but recommended -->\n\u003C\u002FPropertyGroup>\n```\n\n```csharp\n\u002F\u002F Per-file (for incremental adoption on existing codebases):\n#nullable enable    \u002F\u002F enable for this file\n\u002F\u002F ... code with nullable annotations ...\n#nullable disable   \u002F\u002F turn off for legacy code below\n\n\u002F\u002F Or enable annotations but suppress warnings (useful while migrating):\n#nullable enable annotations\n```\n\nAfter enabling, the compiler emits **CS8600–CS8629** warnings for potential null\ndereferences. Common first-time fixes: add `?` to parameters\u002Fproperties that can\nbe null, add null-checks before dereferences, and use the null-forgiving operator\n`!` for cases where you know better than the analyser.\n\n**Rule of thumb:** Set `\u003CNullable>enable\u003C\u002FNullable>` at solution level in\n`Directory.Build.props` for green-field projects. For legacy migrations,\nenable per-project and fix warnings one at a time — the compiler guides you.\n",{"id":372,"difficulty":34,"q":373,"a":374},"null-forgiving-operator","What is the null-forgiving operator `!` and when should you use it?","The **null-forgiving operator** (`!`) suppresses the compiler's nullable warning\nfor an expression. It tells the analyser \"I know this is not null even though\nyou can't prove it.\" It has **no runtime effect** — it is erased at compile time.\n\n```csharp\n#nullable enable\n\nstring? GetName() => null; \u002F\u002F returns null sometimes\n\n\u002F\u002F Compiler warns: possible null\nstring name = GetName(); \u002F\u002F CS8600\n\n\u002F\u002F Null-forgiving — suppress the warning (use with care!):\nstring name = GetName()!; \u002F\u002F no warning — you're asserting it is not null at runtime\n\n\u002F\u002F Legitimate use cases:\n\u002F\u002F 1. Lazy initialisation pattern where you know it's set before use:\nprivate string _label = null!; \u002F\u002F set in constructor flow the analyser can't track\n\n\u002F\u002F 2. Test initialisation (xUnit\u002FNUnit [SetUp] methods):\nprivate MyService _service = null!; \u002F\u002F assigned in SetUp, analyser can't see that\n\n\u002F\u002F 3. After a runtime contract check:\nDebug.Assert(value != null);\nConsole.WriteLine(value!.Length); \u002F\u002F analyser doesn't track Debug.Assert\n```\n\nOverusing `!` defeats the purpose of nullable reference types. Every `!` is a\nclaim you are making — if wrong, it produces a `NullReferenceException` at runtime\nwith no compiler warning.\n\n**Rule of thumb:** Use `!` only where you have a genuine guarantee the analyser\ncannot infer — lazy fields, test setup, post-assertion code. If you find yourself\nusing `!` often, that's a sign to refactor to avoid the null.\n",{"id":376,"difficulty":42,"q":377,"a":378},"null-coalescing-assignment","What is the null-coalescing assignment operator `??=`?","`??=` assigns the right-hand value to the left-hand variable **only if** the\nvariable is currently `null`. It is shorthand for the lazy-initialisation pattern.\n\n```csharp\n\u002F\u002F Without ??=:\nif (_cache == null)\n    _cache = new Dictionary\u003Cstring, string>();\n\n\u002F\u002F With ??=: (C# 8+)\n_cache ??= new Dictionary\u003Cstring, string>();\n\n\u002F\u002F Works with nullable value types too:\nint? count = null;\ncount ??= ComputeCount(); \u002F\u002F only calls ComputeCount() if count is null\n\n\u002F\u002F Chaining in property getters (lazy initialisation):\nprivate List\u003Cstring>? _items;\npublic List\u003Cstring> Items => _items ??= new List\u003Cstring>();\n\u002F\u002F First access creates the list; subsequent accesses return the existing list\n\n\u002F\u002F With method calls — right side evaluated only if left is null:\nstring? result = null;\nresult ??= FetchFromServer(); \u002F\u002F FetchFromServer only called when result is null\n```\n\n`??=` is evaluated left-to-right: check if null, assign only if so. The right-hand\nside is never evaluated when the left side already has a value (short-circuit).\n\n**Rule of thumb:** Use `??=` for simple lazy initialisation of fields and\nproperties. It replaces the `if (x == null) x = ...` pattern cleanly.\n",{"id":380,"difficulty":34,"q":381,"a":382},"pattern-matching-null","How does pattern matching work with null checks in C#?","C# pattern matching (C# 7–11) integrates null checking into switch expressions\nand `is` expressions, producing cleaner null guards than explicit `!= null` checks.\n\n```csharp\n\u002F\u002F is pattern with null check:\nobject? obj = GetObject();\n\nif (obj is string s)  \u002F\u002F only matches if obj is non-null AND is a string\n    Console.WriteLine(s.Length);\n\n\u002F\u002F Switch expression with null arm:\nstring Describe(object? value) => value switch\n{\n    null           => \"nothing\",\n    int n when n \u003C 0 => \"negative number\",\n    int n          => $\"positive number {n}\",\n    string s       => $\"string: {s}\",\n    _              => \"something else\",\n};\n\n\u002F\u002F Not-null pattern (C# 9):\nif (obj is not null)\n    Console.WriteLine(obj.ToString()); \u002F\u002F analyser knows non-null in this branch\n\n\u002F\u002F Combined with property patterns:\nif (user is { Name: not null, Age: > 18 })\n    Console.WriteLine(\"Adult user with a name\");\n```\n\nPattern matching integrates with the nullable flow analysis — after `if (x is T t)`,\nthe compiler knows `t` is non-null inside the branch. This is cleaner than\n`x != null && x is T t` and removes the need for the null-forgiving operator.\n\n**Rule of thumb:** Use `is not null` instead of `!= null` for clarity and\nalignment with the analyser. Use switch expressions with a `null` arm to make\nnull handling explicit and exhaustive.\n",{"id":384,"difficulty":91,"q":385,"a":386},"boxing-nullable","How does boxing work for nullable value types — what gets boxed?","When you box a `Nullable\u003CT>`, the result depends on `HasValue`:\n- If `HasValue` is **true**, the CLR boxes the **underlying `T` value** (not the `Nullable\u003CT>` wrapper).\n- If `HasValue` is **false** (null), boxing produces a **null reference** — not a boxed `Nullable\u003CT>`.\n\n```csharp\nint?   a = 42;\nobject boxedA = a;  \u002F\u002F boxes to a boxed int (System.Int32), not Nullable\u003Cint>\nConsole.WriteLine(boxedA.GetType().Name); \u002F\u002F \"Int32\" — not \"Nullable`1\"\n\nint?   b = null;\nobject boxedB = b;  \u002F\u002F null reference — no heap allocation\nConsole.WriteLine(boxedB == null); \u002F\u002F True\n\n\u002F\u002F Unboxing: cast back to int? or int\nint?  unboxed1 = (int?)boxedA;   \u002F\u002F 42\nint   unboxed2 = (int)boxedA;    \u002F\u002F 42 — can unbox to either\n\n\u002F\u002F Cannot unbox to the \"wrong\" nullable:\n\u002F\u002F double? wrong = (double?)boxedA; \u002F\u002F InvalidCastException — was boxed as int\n```\n\nThis design means `object` round-trips through null perfectly. A `null` nullable\nassigned to `object` gives you `null` back — not a weird \"boxed null\" object.\nIt also means `Nullable\u003Cint>` itself is never observable as a boxed type at runtime.\n\n**Rule of thumb:** Avoid boxing nullable value types in hot paths — like all\nboxing, it causes a heap allocation. The only difference is that a null nullable\nboxes to a true null (no allocation), so at least that case is free.\n",{"id":388,"difficulty":42,"q":389,"a":390},"getvalueordefault","What is `GetValueOrDefault()` on a nullable type and how does it differ from `Value`?","`Value` throws `InvalidOperationException` if `HasValue` is false. `GetValueOrDefault()`\nreturns `default(T)` (or a supplied fallback) if the nullable is null — no exception.\n\n```csharp\nint? n = null;\n\n\u002F\u002F Throws if null:\n\u002F\u002F int x = n.Value; \u002F\u002F InvalidOperationException!\n\n\u002F\u002F Safe alternatives:\nint a = n.GetValueOrDefault();     \u002F\u002F 0 (default int) — no throw\nint b = n.GetValueOrDefault(-1);   \u002F\u002F -1 — custom default — no throw\nint c = n ?? -1;                   \u002F\u002F -1 — equivalent using ?? operator\n\n\u002F\u002F When you KNOW it is non-null, Value is fine:\nint? confirmed = 42;\nif (confirmed.HasValue)\n    Console.WriteLine(confirmed.Value); \u002F\u002F safe — HasValue checked first\n\n\u002F\u002F Common pattern: database int column that defaults to 0 if NULL:\nint quantity = row.GetNullableInt(\"qty\").GetValueOrDefault(0);\n```\n\n`GetValueOrDefault(fallback)` is equivalent to `nullable ?? fallback` — both are\nvalid. The `??` form is usually preferred for brevity. `GetValueOrDefault()` without\nan argument is useful when you want the natural zero\u002Ffalse\u002Fdefault rather than\ninventing a sentinel value.\n\n**Rule of thumb:** Prefer `?? defaultValue` for explicit defaults. Use\n`GetValueOrDefault()` (no arg) when you genuinely want the zero value for the type.\nNever use `.Value` without first checking `.HasValue` or using a null guard.\n",{"id":392,"difficulty":34,"q":393,"a":394},"nullable-arithmetic","How do arithmetic and comparison operators behave with nullable value types?","Arithmetic and comparison operators on nullable value types use **lifted operators**:\nif either operand is `null`, the result is `null` (for arithmetic) or `false`\n(for comparisons other than `!=`). This mirrors SQL's three-value logic.\n\n```csharp\nint? a = 5;\nint? b = null;\n\n\u002F\u002F Arithmetic — null propagates:\nConsole.WriteLine(a + b);   \u002F\u002F null\nConsole.WriteLine(a * 2);   \u002F\u002F 10  (int? * int → int?)\nConsole.WriteLine(b + b);   \u002F\u002F null\n\n\u002F\u002F Comparison — returns bool (not bool?):\nConsole.WriteLine(a > 3);   \u002F\u002F True\nConsole.WriteLine(b > 3);   \u002F\u002F False — not null, just false\nConsole.WriteLine(b == null); \u002F\u002F True — use == null to detect null\n\n\u002F\u002F Equality is special: null == null is True, null == non-null is False\nint? x = null, y = null;\nConsole.WriteLine(x == y);  \u002F\u002F True\n\n\u002F\u002F Pitfall — null comparisons that always return false:\n\u002F\u002F if (b > 0) { ... }  — silently skipped when b is null\n\u002F\u002F Always guard: if (b.HasValue && b.Value > 0) { ... }\n\u002F\u002F          or: if (b > 0 == true) { ... }  — explicit but verbose\n```\n\nThe `==` and `!=` operators are special-cased: `null == null` returns `true`.\nAll other comparisons (`\u003C`, `>`, `\u003C=`, `>=`) return `false` when either side is null.\n\n**Rule of thumb:** Treat null propagation in nullable arithmetic like SQL NULLs.\nBefore performing a meaningful comparison or calculation, check `HasValue` or\nuse `??` to substitute a concrete value.\n",{"id":396,"difficulty":34,"q":397,"a":398},"nullable-in-ternary-and-switch","How should you handle nullable types in ternary expressions and switch statements?","Nullable types work naturally in ternary and switch expressions, but you must be\ncareful that the compiler can infer a common result type and that null arms are\nhandled.\n\n```csharp\nint? score = GetScore(); \u002F\u002F may be null\n\n\u002F\u002F Ternary — result is string (non-nullable) in both arms:\nstring label = score.HasValue\n    ? score.Value >= 60 ? \"Pass\" : \"Fail\"\n    : \"Not taken\";\n\n\u002F\u002F Same with ?? and ?. (often cleaner):\nstring label2 = score switch\n{\n    null       => \"Not taken\",\n    >= 90      => \"Distinction\",\n    >= 60      => \"Pass\",\n    _          => \"Fail\",\n};\n\n\u002F\u002F Nullable bool — three-state switch:\nbool? isConfirmed = GetConfirmation();\nstring status = isConfirmed switch\n{\n    true  => \"Confirmed\",\n    false => \"Rejected\",\n    null  => \"Pending\",\n};\n\n\u002F\u002F Warning: ternary with mismatched nullable\u002Fnon-nullable types:\nint? a = null;\n\u002F\u002F var r = condition ? a : 0; \u002F\u002F r is int? because one arm is int?\n```\n\nSwitch expressions are the cleanest tool for handling nullable types with multiple\nbranches: they force you to be explicit about the null arm, and the compiler warns\nif a case is missing.\n\n**Rule of thumb:** Use a `null` arm in switch expressions when the input is\nnullable. Prefer switch expressions over chains of ternaries for three or more\noutcomes — they are easier to read and exhaustiveness is checked at compile time.\n",{"id":400,"difficulty":34,"q":401,"a":402},"nullable-collections","What is the difference between a nullable collection and a collection of nullable elements?","These are two different concepts that are easy to conflate. A **nullable collection**\n(`List\u003Cint>?`) is a collection reference that can be null. A **collection of nullable\nelements** (`List\u003Cint?>`) is a non-null collection that can hold null int entries.\n\n```csharp\n\u002F\u002F Nullable collection — the LIST reference itself may be null:\nList\u003Cint>? maybeList = null;\nmaybeList?.Add(1);           \u002F\u002F safe — does nothing if list is null\nint count = maybeList?.Count ?? 0; \u002F\u002F 0\n\n\u002F\u002F Collection of nullable ints — the list exists but elements may be null:\nList\u003Cint?> withNulls = new List\u003Cint?> { 1, null, 3 };\nforeach (int? item in withNulls)\n    Console.WriteLine(item ?? -1); \u002F\u002F -1 for the null slot\n\n\u002F\u002F Common confusion: filtering nulls out of a nullable element list:\nList\u003Cint> nonNulls = withNulls\n    .Where(x => x.HasValue)\n    .Select(x => x!.Value)   \u002F\u002F safe: HasValue checked\n    .ToList();                \u002F\u002F { 1, 3 }\n\n\u002F\u002F Or more cleanly with OfType\u003Cint>():\nList\u003Cint> clean = withNulls.OfType\u003Cint>().ToList(); \u002F\u002F { 1, 3 }\n```\n\nIn APIs and data models: use a nullable collection (`IEnumerable\u003CT>?`) when the\nabsence of a list is meaningful (not yet loaded, not applicable). Use\n`IEnumerable\u003CT?>` when the list itself is always present but individual items may\nbe absent.\n\n**Rule of thumb:** Prefer returning an empty collection over a null collection —\nit simplifies callers. Reserve `List\u003CT>?` for cases where null genuinely means\n\"not yet fetched\" or \"not applicable,\" distinct from an empty result.\n",{"id":404,"difficulty":91,"q":405,"a":406},"nullable-flow-analysis","What is nullable flow analysis and how does the C# compiler use it?","**Nullable flow analysis** (C# 8+) is the compiler's ability to track the null\nstate of a variable through control-flow paths — narrowing its inferred nullability\nbased on checks, assignments, and pattern matches. This eliminates false positives\nand enables richer diagnostics.\n\n```csharp\n#nullable enable\n\nstring? GetName() => null;\n\nvoid Example(string? name)\n{\n    \u002F\u002F Before check: 'name' is maybe-null — CS8602 if you use it\n    \u002F\u002F Console.WriteLine(name.Length); \u002F\u002F Warning\n\n    if (name == null) return;          \u002F\u002F early return\n\n    \u002F\u002F After null check: flow analysis knows name is non-null here\n    Console.WriteLine(name.Length);    \u002F\u002F no warning — narrowed to string\n\n    \u002F\u002F Pattern matching also narrows:\n    object? obj = GetObject();\n    if (obj is string s)\n        Console.WriteLine(s.ToUpper()); \u002F\u002F s is non-null string here\n\n    \u002F\u002F ?? assignment narrows too:\n    string resolved = name ?? \"default\";\n    Console.WriteLine(resolved.Length); \u002F\u002F no warning — string, not string?\n}\n\n\u002F\u002F Limitation: the analyser does not cross method boundaries\nvoid DoCheck(string? s) { if (s == null) throw new ArgumentNullException(); }\nvoid Use(string? s)\n{\n    DoCheck(s);\n    \u002F\u002F Console.WriteLine(s.Length); \u002F\u002F still warns — analyser can't see into DoCheck\n    \u002F\u002F Fix: use ArgumentNullException.ThrowIfNull(s); which the analyser understands\n}\n```\n\n**Rule of thumb:** Write code that guides the flow analyser: early returns, guard\nclauses, and `is not null` checks. Use `ArgumentNullException.ThrowIfNull()` instead\nof custom guards so the analyser recognises the null-check pattern.\n",{"description":31},"C# nullable types interview questions — Nullable\u003CT>, null-conditional and null-coalescing operators, nullable reference types, and flow analysis.","dotnet\u002Ffundamentals\u002Fnullable-types","qZtUsaokY9W3aiHutp4Y_EMkmiV6lYfzs0_NWY4BJsM",1782244097432]