[{"data":1,"prerenderedAt":114},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Ffundamentals\u002Fnullable-types":3},{"page":4,"siblings":94,"blog":111},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":6,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fdotnet\u002Ffundamentals\u002Fnullable-types.md","Nullable Types",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,5,"\u002Fdotnet\u002Ffundamentals\u002Fnullable-types",[23,28,32,36,40,44,48,52,56,60,65,69,73,77,81],{"id":24,"difficulty":25,"q":26,"a":27},"nullable-value-types","easy","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":29,"difficulty":25,"q":30,"a":31},"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":33,"difficulty":25,"q":34,"a":35},"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":37,"difficulty":25,"q":38,"a":39},"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":41,"difficulty":14,"q":42,"a":43},"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":45,"difficulty":14,"q":46,"a":47},"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":49,"difficulty":14,"q":50,"a":51},"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":53,"difficulty":25,"q":54,"a":55},"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":57,"difficulty":14,"q":58,"a":59},"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":61,"difficulty":62,"q":63,"a":64},"boxing-nullable","hard","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":66,"difficulty":25,"q":67,"a":68},"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":70,"difficulty":14,"q":71,"a":72},"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":74,"difficulty":14,"q":75,"a":76},"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":78,"difficulty":14,"q":79,"a":80},"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":82,"difficulty":62,"q":83,"a":84},"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",15,null,{"description":11},"C# nullable types interview questions — Nullable\u003CT>, null-conditional and null-coalescing operators, nullable reference types, and flow analysis.","dotnet\u002Ffundamentals\u002Fnullable-types","Fundamentals","fundamentals","2026-06-22","qZtUsaokY9W3aiHutp4Y_EMkmiV6lYfzs0_NWY4BJsM",[95,99,102,106,110],{"subtopic":96,"path":97,"order":98},"CLR Runtime","\u002Fdotnet\u002Ffundamentals\u002Fclr-runtime",1,{"subtopic":100,"path":101,"order":12},"Value vs Reference Types","\u002Fdotnet\u002Ffundamentals\u002Fvalue-vs-reference-types",{"subtopic":103,"path":104,"order":105},"Generics","\u002Fdotnet\u002Ffundamentals\u002Fgenerics",3,{"subtopic":107,"path":108,"order":109},"LINQ","\u002Fdotnet\u002Ffundamentals\u002Flinq",4,{"subtopic":6,"path":21,"order":20},{"path":112,"title":113},"\u002Fblog\u002Fdotnet-nullable-types-null-safety-csharp8","Null Safety in C#: Nullable Types and Nullable Reference Types",1782244117907]