Skip to content

Everyday C#: Null safety tutorials#53542

Open
BillWagner wants to merge 13 commits into
dotnet:mainfrom
BillWagner:null-safety-tutorials
Open

Everyday C#: Null safety tutorials#53542
BillWagner wants to merge 13 commits into
dotnet:mainfrom
BillWagner:null-safety-tutorials

Conversation

@BillWagner
Copy link
Copy Markdown
Member

@BillWagner BillWagner commented May 4, 2026

Fixes #52838

If you review commit-by-commit, the 4th commit is updating links, and can be skimmed.

Note for reviewers

The following files had the major changes. Others were updating links:

  • docs/csharp/fundamentals/null-safety/nullable-reference-types.md (Markdown, Preview)
  • docs/csharp/fundamentals/null-safety/resolve-warnings.md (Markdown, Preview)
  • docs/csharp/advanced-topics/update-applications/nullable-migration-strategies.md (Markdown, Preview)
  • docs/csharp/fundamentals/tutorials/nullable-reference-types.md‎ Markdown, Preview

Note that the migration strategies moved out from fundamentals. It's not meant for new C# developers.

Plan: PR 9 — Null safety: NRT, warnings, migration, tutorial

Consolidate the existing NRT concept article + tutorial into a fundamentals-style concept + tutorial pair, and split nullable-migration-strategies.md into two new fundamentals concept articles. Add redirects, update docs/csharp/toc.yml. Depends on PR 8 (#53509) landing the Null safety toc node first.

Phases

Phase 1 — Concept articles (parallel-safe)

  1. fundamentals/null-safety/nullable-reference-types.md — apply template-concept.md. Body from docs/csharp/nullable-references.md, with conceptual prose lifted from the existing tutorial. 4-tier audience tip, ms.topic: concept-article, ai-usage: ai-assisted. All code via :::code::: snippet refs.
  2. fundamentals/null-safety/resolve-warnings.md — concept article. Pull "Understand contexts and warnings" / "Address warnings" / "Enable type annotations" / "Attributes extend type annotations" sections from docs/csharp/nullable-migration-strategies.md. Frame as 5 resolution techniques with worked examples (NOT a per-CS86xx catalog). Cross-link to docs/csharp/language-reference/compiler-messages/nullable-warnings.md for per-warning lookup.
  3. fundamentals/null-safety/migration-strategies.md — concept article. Pull "Plan your migration" / "Next steps" / intro from docs/csharp/nullable-migration-strategies.md. Cover the four default-context strategies and recommended phased order.

Phase 2 — Tutorial (depends on Phase 1 cross-links)

  1. fundamentals/tutorials/nullable-reference-types.md — apply template-tutorial.md. Source: docs/csharp/tutorials/nullable-reference-types.md. Restructure to template (checklist, Prerequisites, numbered task H2s, "Get the code", "Next step"). Trim concept-duplicating prose; replace with cross-links.

Phase 3 — Snippets (parallel-safe with Phases 1–2)

  1. New snippet projects under docs/csharp/fundamentals/null-safety/snippets/{nullable-reference-types,resolve-warnings,migration-strategies}/. Each minimal .csproj, <Nullable>enable</Nullable>, latest TFM. Build + execute per docs copilot-instructions.md.
  2. Move docs/csharp/tutorials/snippets/NullableIntroduction/docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/. Update tutorial :::code::: source paths.

Phase 4 — TOC + redirects + deletes (depends on Phases 1–3)

  1. docs/csharp/toc.yml: remove lines 205–206 (legacy tutorial) and 293–296 (legacy concepts); add 4 new entries inside the Null safety node from PR 8.
  2. .openpublishing.redirection.csharp.json: add 3 new redirects (alphabetical insertion); repoint existing redirects on lines 3126, 5389, 5447 (currently target /dotnet/csharp/nullable-migration-strategies) to new fundamentals URL to avoid chains. Run sort-redirects skill.
  3. Delete docs/csharp/nullable-references.md, docs/csharp/nullable-migration-strategies.md, docs/csharp/tutorials/nullable-reference-types.md.

Phase 5 — Cross-link cleanup (depends on Phase 4)

  1. Repoint inbound links across docs/. Critical: docs/csharp/language-reference/compiler-messages/nullable-warnings.md has multiple links to moved files.

Verification

  1. Each new snippet project builds: dotnet build; tutorial sample runs: dotnet run.
  2. git grep -n "nullable-references.md\|nullable-migration-strategies.md\|tutorials/nullable-reference-types.md" returns 0 hits in docs/.
  3. python -m json.tool .openpublishing.redirection.csharp.json validates JSON.
  4. Visual confirmation of TOC ordering against project map.
  5. Every new/edited article has ai-usage: ai-assisted; no F1/helpviewer keywords; 4-tier audience tip near top.
  6. Spot-check 2–3 samples per article for Everyday C# saturation (file-scoped namespaces, primary constructors, collection expressions, raw strings, var).
  7. After PR opens, resolve any OpenPublishing.Build warnings introduced.

Decisions

  • Split confirmed: planning content → migration-strategies.md; address-warnings/contexts → resolve-warnings.md. Reference page stays untouched.
  • resolve-warnings.md is concept-level (5 techniques) — does not duplicate the per-CS86xx catalog; cross-links to it.
  • Move + revise in same PR per overall plan.
  • ~14 files total — slightly above the ~10 guideline, acceptable per project guideline ("guideline, not a strict rule").

Further considerations

  1. PR 8 merge ordering. Recommendation: wait for PR 8 to merge, then rebase PR 9 onto it (avoids toc conflicts). Alternative: include the Null safety toc node creation in PR 9 (would conflict with PR 8 at merge).
  2. Snippet reuse. Recommendation: do NOT share with language-reference/compiler-messages/snippets/null-warnings/. Create fresh, smaller fundamentals-style snippets so reference vs. fundamentals can evolve independently.
  3. Tutorial vs concept article overlap. Recommendation: trim concept-duplicating prose from the tutorial and add a short "Before you start, see Nullable reference types" pointer near the top.

Internal previews

Toggle expand/collapse
📄 File 🔗 Preview link
docs/core/compatibility/sdk/6.0/csharp-template-code.md C# code in templates not supported by earlier versions
docs/core/extensions/windows-service.md Create Windows Service using BackgroundService
docs/core/whats-new/dotnet-5.md What's new in .NET 5
docs/core/whats-new/dotnet-core-3-0.md What's new in .NET Core 3.0
docs/csharp/advanced-topics/update-applications/nullable-migration-strategies.md Nullable migration strategies
docs/csharp/fundamentals/null-safety/index.md docs/csharp/fundamentals/null-safety/index
docs/csharp/fundamentals/null-safety/null-operators.md C# null operators
docs/csharp/fundamentals/null-safety/nullable-reference-types.md Nullable reference types
docs/csharp/fundamentals/null-safety/nullable-value-types.md Nullable value types: C# Fundamentals
docs/csharp/fundamentals/null-safety/resolve-warnings.md Resolve nullable warnings
docs/csharp/fundamentals/tutorials/nullable-reference-types.md Tutorial: Express your design intent with nullable and non-nullable reference types
docs/csharp/language-reference/attributes/nullable-analysis.md docs/csharp/language-reference/attributes/nullable-analysis
docs/csharp/language-reference/builtin-types/arrays.md Arrays
docs/csharp/language-reference/builtin-types/nullable-reference-types.md "Nullable reference types"
docs/csharp/language-reference/compiler-messages/interface-implementation-errors.md Resolve errors and warnings related to members that implement an interface
docs/csharp/language-reference/compiler-messages/nullable-warnings.md Nullable reference type warnings
docs/csharp/language-reference/compiler-options/language.md "Compiler Options - language feature rules"
docs/csharp/language-reference/keywords/where-generic-type-constraint.md where (generic type constraint) (C# Reference)
docs/csharp/language-reference/operators/null-forgiving.md docs/csharp/language-reference/operators/null-forgiving
docs/csharp/toc.yml Taken from https://github.com/dotnet/roslyn/wiki/Samples-and-Walkthroughs
docs/csharp/tour-of-csharp/tips-for-java-developers.md Tips for Java Developers
docs/csharp/tour-of-csharp/tips-for-javascript-developers.md Roadmap for JavaScript and TypeScript developers learning C#
docs/csharp/tour-of-csharp/tips-for-python-developers.md Python
docs/framework/debug-trace-profile/code-contracts.md docs/framework/debug-trace-profile/code-contracts
docs/fundamentals/code-analysis/style-rules/ide0370.md Remove unnecessary suppression (IDE0370)
docs/fundamentals/syslib-diagnostics/syslib1026.md SYSLIB1026 error

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restructures the C# nullable reference types (NRT) documentation into a Fundamentals “null safety” concept + tutorial set, moves/creates supporting snippet projects, and updates inbound links and TOC entries to point at the new locations.

Changes:

  • Adds new Fundamentals null-safety concept articles (NRT overview, warning resolution, and migration strategies) plus a Fundamentals tutorial and snippet projects.
  • Removes legacy C# NRT concept/tutorial pages and updates docs/csharp/toc.yml accordingly.
  • Repoints links across C#, Core, Framework, and analyzer docs to the new Fundamentals URLs.

Reviewed changes

Copilot reviewed 39 out of 42 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
docs/fundamentals/syslib-diagnostics/syslib1026.md Updates NRT link target to the new Fundamentals article.
docs/fundamentals/code-analysis/style-rules/ide0370.md Updates NRT link target to the new Fundamentals article.
docs/framework/debug-trace-profile/code-contracts.md Updates guidance link to point at the new Fundamentals NRT article.
docs/csharp/tutorials/snippets/NullableIntroduction/SurveyQuestion.cs Removes legacy tutorial snippet file as part of moving the tutorial snippets.
docs/csharp/tutorials/nullable-reference-types.md Removes legacy tutorial page in favor of the new Fundamentals tutorial.
docs/csharp/tour-of-csharp/tips-for-python-developers.md Repoints “Nullable types” link to the new Fundamentals NRT article.
docs/csharp/tour-of-csharp/tips-for-javascript-developers.md Repoints nullable types link to the new Fundamentals NRT article.
docs/csharp/tour-of-csharp/tips-for-java-developers.md Repoints nullable types links to the new Fundamentals NRT article.
docs/csharp/toc.yml Removes legacy tutorial entry and adds a “Null safety” section pointing at new Fundamentals pages.
docs/csharp/nullable-references.md Removes legacy NRT concept page replaced by Fundamentals content.
docs/csharp/nullable-migration-strategies.md Removes legacy migration strategies page replaced by Fundamentals content.
docs/csharp/language-reference/operators/null-forgiving.md Updates links to the new Fundamentals tutorial and NRT overview.
docs/csharp/language-reference/keywords/where-generic-type-constraint.md Updates nullable context link to point at the new Fundamentals NRT overview.
docs/csharp/language-reference/compiler-options/language.md Updates nullable contexts/migration links to new Fundamentals pages.
docs/csharp/language-reference/compiler-messages/nullable-warnings.md Updates links to new Fundamentals NRT overview and migration page.
docs/csharp/language-reference/compiler-messages/interface-implementation-errors.md Updates NRT link to the new Fundamentals NRT overview.
docs/csharp/language-reference/builtin-types/nullable-reference-types.md Updates migration strategies link to new Fundamentals page.
docs/csharp/language-reference/builtin-types/arrays.md Updates NRT links to the new Fundamentals NRT overview (including known pitfalls anchor).
docs/csharp/language-reference/attributes/nullable-analysis.md Updates generics section link to the new Fundamentals NRT overview.
docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/SurveyRun.cs Adds moved tutorial snippet code for SurveyRun.
docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/SurveyResponse.cs Updates moved tutorial snippet code (uses primary constructor and snippet markers).
docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/SurveyQuestion.cs Adds moved tutorial snippet code for SurveyQuestion.
docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/Program.cs Adds moved tutorial snippet code for the top-level program and snippet markers.
docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/NullableIntroduction.csproj Updates tutorial snippet project to net10.0.
docs/csharp/fundamentals/tutorials/nullable-reference-types.md Adds new Fundamentals tutorial page for nullable reference types.
docs/csharp/fundamentals/null-safety/snippets/resolve-warnings/resolve-warnings.csproj Adds new snippet project for the “Resolve warnings” article.
docs/csharp/fundamentals/null-safety/snippets/resolve-warnings/project-snippet.xml Adds minimal XML snippet used to show <Nullable>enable</Nullable>.
docs/csharp/fundamentals/null-safety/snippets/resolve-warnings/Program.cs Adds runnable examples backing the “Resolve warnings” article snippets.
docs/csharp/fundamentals/null-safety/snippets/nullable-reference-types/project-snippet.xml Adds minimal XML snippet used to show <Nullable>enable</Nullable>.
docs/csharp/fundamentals/null-safety/snippets/nullable-reference-types/Program.cs Adds runnable examples backing the “Nullable reference types” article snippets.
docs/csharp/fundamentals/null-safety/snippets/nullable-reference-types/nullable-reference-types.csproj Adds new snippet project for the NRT overview article.
docs/csharp/fundamentals/null-safety/snippets/migration-strategies/project-snippet.xml Adds minimal XML snippet for migration strategy examples.
docs/csharp/fundamentals/null-safety/snippets/migration-strategies/Program.cs Adds runnable examples backing the migration strategies article snippets.
docs/csharp/fundamentals/null-safety/snippets/migration-strategies/migration-strategies.csproj Adds new snippet project for the migration strategies article.
docs/csharp/fundamentals/null-safety/resolve-warnings.md Adds new Fundamentals concept article describing warning-resolution techniques.
docs/csharp/fundamentals/null-safety/nullable-reference-types.md Adds new Fundamentals concept article for nullable reference types.
docs/csharp/fundamentals/null-safety/migration-strategies.md Adds new Fundamentals concept article for migration strategies.
docs/core/whats-new/dotnet-core-3-0.md Updates tutorial link to the new Fundamentals tutorial location.
docs/core/whats-new/dotnet-5.md Updates NRT link to the new Fundamentals NRT overview.
docs/core/extensions/windows-service.md Updates NRT link to the new Fundamentals NRT overview.
docs/core/compatibility/sdk/6.0/csharp-template-code.md Updates NRT link to the new Fundamentals NRT overview.

Comment thread docs/csharp/fundamentals/null-safety/nullable-reference-types.md
Comment thread docs/csharp/language-reference/operators/null-forgiving.md
Comment thread docs/csharp/language-reference/compiler-options/language.md Outdated
Comment thread docs/csharp/language-reference/compiler-messages/nullable-warnings.md Outdated
Comment thread docs/csharp/fundamentals/null-safety/resolve-warnings.md
Comment thread docs/csharp/fundamentals/null-safety/snippets/resolve-warnings/Program.cs Outdated
BillWagner added 9 commits May 8, 2026 14:53
1. `fundamentals/null-safety/nullable-reference-types.md` — apply `template-concept.md`. Body from [docs/csharp/nullable-references.md](docs/csharp/nullable-references.md), with conceptual prose lifted from the existing tutorial. 4-tier audience tip, `ms.topic: concept-article`, `ai-usage: ai-assisted`. All code via `:::code:::` snippet refs.
2. `fundamentals/null-safety/resolve-warnings.md` — concept article. Pull "Understand contexts and warnings" / "Address warnings" / "Enable type annotations" / "Attributes extend type annotations" sections from [docs/csharp/nullable-migration-strategies.md](docs/csharp/nullable-migration-strategies.md). Frame as **5 resolution techniques** with worked examples (NOT a per-CS86xx catalog). Cross-link to [docs/csharp/language-reference/compiler-messages/nullable-warnings.md](docs/csharp/language-reference/compiler-messages/nullable-warnings.md) for per-warning lookup.
3. `fundamentals/null-safety/migration-strategies.md` — concept article. Pull "Plan your migration" / "Next steps" / intro from [docs/csharp/nullable-migration-strategies.md](docs/csharp/nullable-migration-strategies.md). Cover the four default-context strategies and recommended phased order.
4. `fundamentals/tutorials/nullable-reference-types.md` — apply `template-tutorial.md`. Source: [docs/csharp/tutorials/nullable-reference-types.md](docs/csharp/tutorials/nullable-reference-types.md). Restructure to template (checklist, Prerequisites, numbered task H2s, "Get the code", "Next step"). Trim concept-duplicating prose; replace with cross-links.
. New snippet projects under `docs/csharp/fundamentals/null-safety/snippets/{nullable-reference-types,resolve-warnings,migration-strategies}/`. Each minimal `.csproj`, `<Nullable>enable</Nullable>`, latest TFM. Build + execute per docs `copilot-instructions.md`.
6. Move `docs/csharp/tutorials/snippets/NullableIntroduction/` → `docs/csharp/fundamentals/tutorials/snippets/NullableIntroduction/`. Update tutorial `:::code:::` source paths.
[docs/csharp/toc.yml](docs/csharp/toc.yml): remove lines 205–206 (legacy tutorial) and 293–296 (legacy concepts); add 4 new entries inside the `Null safety` node from PR 8.
8. [.openpublishing.redirection.csharp.json](.openpublishing.redirection.csharp.json): add 3 new redirects (alphabetical insertion); repoint existing redirects on lines 3126, 5389, 5447 (currently target `/dotnet/csharp/nullable-migration-strategies`) to new fundamentals URL to avoid chains. Run `sort-redirects` skill.
9. Delete [docs/csharp/nullable-references.md](docs/csharp/nullable-references.md), [docs/csharp/nullable-migration-strategies.md](docs/csharp/nullable-migration-strategies.md), [docs/csharp/tutorials/nullable-reference-types.md](docs/csharp/tutorials/nullable-reference-types.md).
***When reviewing commit-by-commit, this can be skimmed***

10. Repoint inbound links across `docs/`. Critical: [docs/csharp/language-reference/compiler-messages/nullable-warnings.md](docs/csharp/language-reference/compiler-messages/nullable-warnings.md) has multiple links to moved files.
Review the drafts, make several changes in style, tone and substance.
…ions

Relocate the migration article out of fundamentals/null-safety into a new Advanced topics > Update existing apps subsection. Rename to nullable-migration-strategies.md, repath snippets, update inbound links from sibling articles and language-reference, repoint redirects, and add a redirect from the vacated path.
@BillWagner BillWagner force-pushed the null-safety-tutorials branch from 13fb2be to 08fa13e Compare May 8, 2026 18:55
BillWagner added 4 commits May 8, 2026 15:13
Rename language-reference compiler-messages H1 'Resolve nullable warnings' to 'Nullable reference type warnings' to avoid collision with the new fundamentals article. Rename the H2 'Enable nullable reference types' to 'Nullable context' so existing inbound bookmarks #nullable-context resolve.
@BillWagner BillWagner marked this pull request as ready for review May 8, 2026 19:45
@BillWagner BillWagner requested review from a team and gewarren as code owners May 8, 2026 19:45
@BillWagner
Copy link
Copy Markdown
Member Author

Note for reviewers:

During a rebase, the redirection file got quite mangled. The diffs look better if you hide whitespace, but Copilot still decided to sort all the entries so the diffs are much larger than expected. I did verify the substantive changes by hand.

- *Null-state analysis* tracks whether the value of an expression is *not-null* or *maybe-null* at each point in your code.
- *Attributes* on APIs describe more nuanced contracts, such as "this argument can be `null`, but the return value is null only when the argument is null."

The compiler combines these signals to produce diagnostics. Warnings on a non-nullable variable mean the variable might receive `null`. Warnings on a nullable variable mean the code might *dereference* it without a null check. *Dereference* means to use the value the variable refers to—for example, to call a method on it (`variable.Method()`), read a property (`variable.Property`), or index into it (`variable[0]`). Dereferencing `null` throws an exception at run time. Either kind of warning means the code's behavior doesn't match its stated design.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be slightly clearer for the new coder

Suggested change
The compiler combines these signals to produce diagnostics. Warnings on a non-nullable variable mean the variable might receive `null`. Warnings on a nullable variable mean the code might *dereference* it without a null check. *Dereference* means to use the value the variable refers to—for example, to call a method on it (`variable.Method()`), read a property (`variable.Property`), or index into it (`variable[0]`). Dereferencing `null` throws an exception at run time. Either kind of warning means the code's behavior doesn't match its stated design.
The compiler combines these signals to produce diagnostics. Warnings on a non-nullable variable mean the variable might receive `null`. Warnings on a nullable variable mean the code might *dereference* it without a null check. *Dereference* means to use the value the variable refers to—for example, to call a method on it (`variable.Method()`), read a property (`variable.Property`), or index into it (`variable[0]`). Dereferencing a variable that has a value of `null` throws an exception at run time. Either kind of warning means the code's behavior doesn't match its stated design.


## Nullable context

For new projects targeting .NET 6 or later, the project template includes the `<Nullable>enable</Nullable>` element by default. To enable the feature in an existing project, add it manually:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't say this. .NET ships with a lot of templates and there is no guarantee that the code a user is looking at has this in the project. Further, projects migrated from .NET Framework won't have this enabled.

I would just say that if they're working with a newly created project, it's more than likely that nullable context is enabled in the project file with <Nullable>enable</Nullable>. And that if it's missing, it can be added manually...


## Express intent with annotations

When you enable the feature, every reference type variable is *non-nullable* by default. Append `?` to declare a *nullable* reference type:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
When you enable the feature, every reference type variable is *non-nullable* by default. Append `?` to declare a *nullable* reference type:
When the feature is enabled, every reference type variable is *non-nullable* by default. Append `?` to declare a *nullable* reference type:


Use the annotation to make required and optional values visible in the type system. The following `Person` type declares `FirstName` and `LastName` as non-nullable—every person must have both—and `MiddleName` as nullable, because not everyone has one:

:::code language="csharp" source="snippets/nullable-reference-types/Program.cs" id="DesignIntent":::
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

This code could be improved to show a second person without a middle name. Further, the output of the ToString should be printed as a comment at the end. That will really drive home the concept of how the null check changed the code path. Although that might not really be the focus of the code... So, total nit 😁

- *not-null* — the expression is known to be not `null`.
- *maybe-null* — the expression might be `null`.

A local variable's null-state can change on any line of code. The rule is simple: after an assignment, the variable takes on the null-state of the expression on the right-hand side; after a check against `null`, the variable takes on the null-state implied by the branch you're in. For example, assigning a non-null literal or checking that a variable isn't `null` makes it *not-null*, while assigning `null`, the result of a method whose return type is nullable, or a variable that's currently *maybe-null* makes it *maybe-null*:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a "simple" rule, I find this entire paragraph very complex and verbose. I iterated with copilot to simplify the wording. How's this?

Suggested change
A local variable's null-state can change on any line of code. The rule is simple: after an assignment, the variable takes on the null-state of the expression on the right-hand side; after a check against `null`, the variable takes on the null-state implied by the branch you're in. For example, assigning a non-null literal or checking that a variable isn't `null` makes it *not-null*, while assigning `null`, the result of a method whose return type is nullable, or a variable that's currently *maybe-null* makes it *maybe-null*:
A local variable's null-state is updated as the compiler analyzes your code. Two things change it: **assignments** and **null checks**. After an assignment, the variable's null-state matches the expression on the right-hand side—if the expression is null or nullable, the variable becomes maybe-null; if the expression is a non-null literal, the variable becomes not-null. After a null check, the variable's null-state reflects whichever branch is taken.:


## Generics

Generic type parameters without constraints can be reference types or value types, so `T?` follows different rules depending on the type argument:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should link to a generics overview. This is another feature of .NET that is interpreting things differently. If the user is unfamiliar with generics, they may want to know more before reading further.

Comment on lines +90 to +100
- If `T` is a non-nullable reference type, `T?` is the corresponding nullable reference type.
- If `T` is a value type, `T?` is the same value type. (Add the `struct` constraint to get <xref:System.Nullable%601> behavior.)
- If `T` is already nullable, `T?` is the same type.

Constraints refine the rules:

- `class` requires a non-nullable reference type.
- `class?` allows either a nullable or a non-nullable reference type.
- `struct` requires a non-nullable value type. `T?` then means <xref:System.Nullable%601>.
- `notnull` requires a non-nullable reference or value type.
- A base class constraint such as `where T : BaseType` requires a non-nullable reference type that derives from `BaseType`. Append `?` (`where T : BaseType?`) to allow either a nullable or a non-nullable reference type.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this very confusing because it's talking about the generic type passed and also the use of that generic type. Or is it? I'm not sure. It's not differentiating what it's talking about, so it makes it sound like the types used are declared as nullable or not-nullable, because when you use T it can be T or T?. So seeing T around is just confusing.

For example, on the first statement:

  • If T is a non-nullable reference type, T? is the corresponding nullable reference type.

Is it talking about the T provided on the declaration? void Method<T>(). If so, how could it be a non-nullable reference type? All reference types are nullable, right? A string variable is always nullable because it's a reference type. It's the enablement of the nullability feature and declaration of the variable that give it a non-nullable characteristic, right?


Two patterns can leave a non-nullable reference holding `null` without a warning. Both patterns are limitations of the static analysis, not bugs in your code.

**Default structs.** You can create a struct with non-nullable reference fields by using `default` or `new()`. This approach leaves the struct's fields uninitialized:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be an H3 section


The fields hold `null` at run time, but the compiler doesn't warn. The same pitfall extends to *arrays of structs*: a new array initializes every element to the struct's default value, so each element's non-nullable reference fields start as `null`. If you must use a struct, prefer [required members](../../language-reference/keywords/required.md) (members the caller must initialize through an object initializer) or a parameterized constructor that callers must invoke, and populate every element of any array you create.

**Arrays of references.** A new array of a non-nullable reference type contains all `null` elements until you assign each one:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be an H3 section


:::code language="csharp" source="snippets/nullable-reference-types/Program.cs" id="DefaultStructPitfall":::

The fields hold `null` at run time, but the compiler doesn't warn. The same pitfall extends to *arrays of structs*: a new array initializes every element to the struct's default value, so each element's non-nullable reference fields start as `null`. If you must use a struct, prefer [required members](../../language-reference/keywords/required.md) (members the caller must initialize through an object initializer) or a parameterized constructor that callers must invoke, and populate every element of any array you create.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a new paragraph

Suggested change
The fields hold `null` at run time, but the compiler doesn't warn. The same pitfall extends to *arrays of structs*: a new array initializes every element to the struct's default value, so each element's non-nullable reference fields start as `null`. If you must use a struct, prefer [required members](../../language-reference/keywords/required.md) (members the caller must initialize through an object initializer) or a parameterized constructor that callers must invoke, and populate every element of any array you create.
The fields hold `null` at run time, but the compiler doesn't warn.
The same pitfall extends to *arrays of structs*: a new array initializes every element to the struct's default value, so each element's non-nullable reference fields start as `null`. If you must use a struct, prefer [required members](../../language-reference/keywords/required.md) (members the caller must initialize through an object initializer) or a parameterized constructor that callers must invoke, and populate every element of any array you create.

Also, I don't know how "required" could work? Does that force the user to create every element of the array at declaration?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Everyday C#] Phase C, PR 9: Null safety: NRT, warnings, migration, tutorial

3 participants