Skip to content
Development
Skill

/csharp-nullable-reference-types

Guidelines for introducing and using nullable reference types (NRT) and System.Diagnostics.CodeAnalysis nullable attributes in C# / .NET codebases. Covers the nullability model, flow analysis, the null-forgiving operator, API design rules, the full attribute catalog (AllowNull,

From plugin
dotnet-skills
1.1k36 skills6 agents
Install
$ npx -y skills add aaronontheweb/dotnet-skills --skill csharp-nullable-reference-types --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/csharp-nullable-reference-types

Context preview

The summary Claude sees to decide when to auto-load this skill.

Guidelines for introducing and using nullable reference types (NRT) and System.Diagnostics.CodeAnalysis nullable attributes in C# / .NET codebases. Covers the nullability model, flow analysis, the null-forgiving operator, API design rules, the full attribute catalog (AllowNull,

SKILL.md

csharp-nullable-reference-types.SKILL.md
name: csharp-nullable-reference-types
description: Guidelines for introducing and using nullable reference types (NRT) and System.Diagnostics.CodeAnalysis nullable attributes in C# / .NET codebases. Covers the nullability model, flow analysis, the null-forgiving operator, API design rules, the full attribute catalog (AllowNull, DisallowNull, MaybeNull, NotNull, NotNullWhen, MaybeNullWhen, NotNullIfNotNull, MemberNotNull, MemberNotNullWhen, DoesNotReturn, DoesNotReturnIf), the C# 14 field keyword, incremental migration of legacy codebases, and a code-generation checklist.
version: 1.0.0
tags:
  - csharp
  - nullable
  - nrt
  - code-quality
  - api-design

C# Nullable Reference Types

When to Use

  • Introducing nullable reference types (NRT) into a codebase that has not yet adopted them
  • Writing or refactoring C# code that uses `T?` / nullable annotations
  • Annotating APIs with `System.Diagnostics.CodeAnalysis` nullable attributes
  • Designing public/internal APIs where nullability contracts matter
  • Wrapping unannotated or legacy APIs so downstream callers still benefit from NRT
  • Reviewing code for correct null-state analysis, guard helpers, and the `field` keyword

Core Goals

  • Prevent `NullReferenceException` at runtime by making null intent explicit in signatures.
  • Express contracts the type system cannot represent directly using the official nullable attributes.
  • Adopt NRT incrementally in legacy codebases without a big-bang rewrite.

Core Nullability Model

Non-nullable vs nullable

  • `string` — non-nullable reference. The compiler assumes instances are never `null`; assigning `null` or a maybe-null value produces a warning.
  • `string?` — nullable reference. The variable may be `null`; the compiler requires a null check before dereference.
string name = "Alice";
name = null;          // Warning: assigning null to non-nullable.

string? nickname = null;
Console.WriteLine(nickname.Length); // Warning: possible null dereference.

Null-state analysis (flow)

The compiler tracks whether a reference is *definitely non-null* or *maybe null*. Null checks and assignments update this state.

string? message = GetMessageOrNull();

if (message != null)
{
    // message is definitely non-null in this block.
    Console.WriteLine(message.Length);
}

// Outside the if, message is maybe null again.

Introduce explicit null checks (`if (x != null)`, `is not null`, pattern matching) before dereferencing nullable values. Narrow nullability early and keep the non-null state alive. **Null-conditional assignment (C# 14)** lets you write `customer?.Order = CreateOrder();` — the right side is evaluated only when the receiver is non-null.

Null-forgiving operator (`!`)

`x!` tells the compiler "treat `x` as non-null here." It affects analysis only, not runtime behavior.

  • Use `!` only when a real invariant guarantees non-null and the compiler cannot see it.
  • Do **not** use `!` as a general fix for warnings. Prefer refactoring control flow, adding attributes, or proper member initialization.
_customer = LoadCustomerFromOrm()!; // ORM guarantees this is not null in valid state.

Reorganize code before suppressing warnings

A successful guard clause or pattern match already creates a null-safe region in the current scope. Before adding `!`, make nullable values cross a checked boundary once and keep the remaining code non-nullable:

  • narrow early with a guard clause or pattern match;
  • copy nullable fields or properties to a local before checking, so repeated reads cannot change underneath the analysis;
  • when a method has complex control flow, optionally move the non-null path into a local function or private method with non-nullable parameters;
  • keep nullable handling at the boundary instead of spreading `T?`, repeated checks, or `!` through the implementation.
public void Process(Order? order)
{
    if (order?.Customer is not { } customer)
    {
        return;
    }

    // The pattern match already proved that customer is non-null here.
    Console.WriteLine(customer.Name);
}

Do not extract a function solely to satisfy nullable analysis. Use an explicit non-nullable function boundary when it also simplifies a large or branching implementation. Use `!` only when a real external invariant cannot be represented through control flow, signatures, or nullable-analysis attributes.

Project Configuration

Enable NRT for new code:

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

For legacy codebases, enable incrementally with file-level directives (`#nullable enable`, `#nullable disable`, `#nullable enable warnings`, `#nullable enable annotations`). Treat `CS86xx` nullable warnings as important; consider `TreatWarningsAsErrors` or treating nullable warnings as errors in new projects.

See [nrt-migration-playbook-reference.md](nrt-migration-playbook-reference.md) for the full incremental-adoption strategy, `#nullable` directive reference, legacy interop, and known static-analysis limitations (arrays, `default(struct)`).

API Design Rules (Signatures)

These rules apply to public and internal APIs and to models.

**Parameters** — if `null` is not allowed, use a non-nullable type and add a runtime guard for public APIs:

public void SendEmail(string recipient)
{
    ArgumentNullException.ThrowIfNull(recipient);
    // Implementation
}

If `null` is allowed and meaningful, use `T?`, document how `null` is interpreted, and implement correct `null` behavior.

**Return types** — `Customer` when the method never returns `null`; `Customer?` when it can legitimately return `null` (callers must check, and the compiler enforces it).

public Customer GetRequiredCustomer(Guid id);  // Throws on failure.
public Customer? TryGetCustomer(Guid id);       // Returns null on failure.

**Properties and fields** — follow the same rules as parameters and return types. Non-nul

Read more
Ships withdotnet-skills

A comprehensive AI coding plugin with 30 skills and 5 specialized agents for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.

Get the whole plugin