null 許容参照型

Null 許容未指定のコンテキスト内では、すべての参照型は Null 許容でした。 "Null 許容参照型" とは、Null 許容認識コンテキストで有効になっている機能のグループを指します。これを使用すると、コードでランタイムにより System.NullReferenceException がスローされる可能性を最小限に抑えることができます。 "Null 許容参照型" には、これらの例外を回避するために役立つ 3 つの機能が含まれており、これには参照型を明示的に "Null 許容" とマークする機能が含まれます。

  • 変数を逆参照する前に、それが null になる場合があるかどうかを判断する、改善された静的フロー分析。
  • フロー分析が "null 状態" を決定するために API に注釈を付ける属性。
  • 開発者が変数に意図された "null 状態" を明示的に宣言するために使用する変数の注釈。

コンパイラは、コンパイル時にコード内のすべての式の "null 状態" を追跡します。 "null 状態" には、次の 3 つの値のいずれかが含まれます。

  • "null 以外": この式は null 以外であることがわかっています。
  • "null の可能性あり": この式は null になる場合があります。
  • "未指定": コンパイラはこの式の null 状態を判断することができません。

変数の注釈は、参照型変数の "NULL 値の許容" を決定します。

  • "null 非許容": この変数に null 値または "null の可能性あり" の式を割り当てると、コンパイラは警告を出します。 "null 非許容" の変数において、既定の null 状態は "null 以外" です。
  • "Null 許容": この変数には null 値または "null の可能性あり" の式を割り当てることができます。 この変数の null 状態が "null の可能性あり" の場合、この変数を逆参照すると、コンパイラは警告を出します。 この変数の既定の null 状態は "null の可能性あり" です。
  • "未指定": この変数には null 値または "null の可能性あり" の式を割り当てることができます。 この変数を逆参照する場合、またはこの変数に "null の可能性あり" の式を割り当てる場合、コンパイラは警告を出しません。

"未指定" の null 状態と "未指定" の NULL 値の許容は、Null 許容参照型が導入される前の動作と一致します。 それらの値は、移行中、またはご利用のアプリが Null 許容参照型を有効にしていないライブラリを使用する場合に役立ちます。

既存のプロジェクトでは、Null 状態の分析と変数の注釈が既定で無効になっています。これは、すべての参照型が引き続き Null 許容になることを意味します。 .NET 6 以降、"新しい" プロジェクトでは既定で有効になっています。 "Null 許容" の注釈コンテキストを宣言してこれらの機能を有効にする方法については、「Null 許容コンテキスト」を参照してください。

この記事の残りの部分では、コードが null 値を逆参照している可能性がある場合に、それら 3 つの機能領域がどのようにして警告を生成するかについて説明します。 変数を逆参照すると、次の例に示すように、. (ドット) 演算子を使用してメンバーの 1 つにアクセスできることになります。

string message = "Hello, World!";
int length = message.Length; // dereferencing "message"

値が null である変数を逆参照すると、ランタイムは System.NullReferenceException をスローします。

以下について説明します。

  • コンパイラの null 状態分析: 式が null 以外か、または null の可能性ありかどうかをコンパイラが判断する方法。
  • コンパイラの null 状態分析に、より多くのコンテキストを提供する API に適用される属性
  • 変数の意図に関する情報を提供する Null 許容変数の注釈。 注釈は、メンバー メソッドの先頭で既定の null 状態を設定するフィールドに役立ちます。
  • ジェネリック型引数を制御する規則。 型パラメーターは、参照型または値の型になる場合があるため、新しい制約が追加されました。 ? サフィックスは、Null 許容値の型と Null 許容参照型において異なる方法で実装されています。
  • Null 許容コンテキストは、大規模なプロジェクトを移行するのに役立ちます。 移行時に、ご利用のアプリの一部において Null 許容コンテキストまたは警告を有効にすることができます。 さらに多くの警告に対処した後、プロジェクト全体に対して Null 許容参照型を有効にすることができます。

最後に、struct 型と配列における、null 状態分析の既知の落とし穴について説明します。

また、これらの概念は、「C# での Null Safety」についての Learn モジュールでも確認できます。

null 状態分析

Null 許容参照型が有効な場合、Null 状態分析は参照の "null 状態" を追跡します。 式は "null 以外" または "null の可能性あり" のいずれかです。 コンパイラは、2 つの方法で変数が "null 以外" であると判断します。

  1. "null 以外" であることがわかっている値がその変数に代入されている。
  2. 変数が null かどうかがチェックされ、そのチェック以降に変更されていない。

Null 許容参照型が有効ではない場合、すべての式の null 状態は "未指定" です。 このセクションの残りの部分では、Null 許容参照型が有効な場合の動作について説明します。

コンパイラが "null 以外" として判断していない変数はすべて "null の可能性あり" と見なされます。 この分析では、誤って null 値を逆参照する場合がある状況において警告を出します。 コンパイラは、"null 状態" に基づいて警告を生成します。

  • 変数が "null 以外" である場合、その変数は安全に逆参照される可能性があります。
  • 変数が "null の可能性あり" である場合、その変数を逆参照する前に null でないことを確認する必要があります。

次の例を確認してください。

string message = null;

// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");

var originalMessage = message;
message = "Hello, World!";

// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");

// warning!
Console.WriteLine(originalMessage.Length);

前の例では、コンパイラは、最初のメッセージが出力される際に message が "null の可能性あり" と判断します。 2 番目のメッセージに対する警告はありません。 コードの最後の行では、originalMessage が null である可能性があるため、警告が生成されます。 次の例は、ノードのツリーをルートにトラバースし、そのトラバーサル中に各ノードを処理する、より実用的な使用を示しています。

void FindRoot(Node node, Action<Node> processNode)
{
    for (var current = node; current != null; current = current.Parent)
    {
        processNode(current);
    }
}

前のコードでは、変数 current の逆参照に関する警告は生成されません。 スタティック分析では、"null の可能性あり" である場合に current が絶対に逆参照されないことが決定されます。 変数 current は、current.Parent にアクセスする前、および currentProcessNode アクションに渡される前に null かどうかがチェックされます。 前の例は、初期化、割り当て、または null との比較の際に、コンパイラがローカル変数の "null 状態" を判断する方法を示しています。

null 状態分析は、呼び出されたメソッド内をトレースしません。 その結果、すべてのコンストラクターによる共通ヘルパー メソッド呼び出し内で初期化されたフィールドは、次のテンプレートを使用して警告を生成します。

null 非許容プロパティ 'name' には、コンストラクター終了時に非 null 値が含まれる必要があります。

これらの警告には、"コンストラクター チェーン" またはヘルパー メソッドの "Null 許容属性" の 2 つの方法のどちらかで対処できます。 次のコードは、それぞれの例を示しています。 Person クラスでは、他のすべてのコンストラクターによって呼び出される共通のコンストラクターを使用しています。 Student クラスには、System.Diagnostics.CodeAnalysis.MemberNotNullAttribute 属性で注釈付けされたヘルパー メソッドがあります。


using System.Diagnostics.CodeAnalysis;

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public Person() : this("John", "Doe") { }
}

public class Student : Person
{
    public string Major { get; set; }

    public Student(string firstName, string lastName, string major)
        : base(firstName, lastName)
    {
        SetMajor(major);
    }

    public Student(string firstName, string lastName) :
        base(firstName, lastName)
    {
        SetMajor();
    }

    public Student()
    {
        SetMajor();
    }

    [MemberNotNull(nameof(Major))]
    private void SetMajor(string? major = default)
    {
        Major = major ?? "Undeclared";
    }
}

Note

C# 10 では、割り当てと null 状態分析を明確にするために、多くの改善策が追加されました。 C# 10 にアップグレードすると、誤検知である null 許容警告が少なくなります。 改善の詳細については、機能仕様の明確な割り当て改善に関するページを参照してください。

Null 許容状態分析とコンパイラによって生成される警告は、null の逆参照によってプログラム エラーを回避するのに役立ちます。 null 許容の警告の解決に関する記事では、コード内で発生する可能性が高い警告を修正する手法について説明しています。

API シグネチャの属性

null 状態分析には、API のセマンティクスを理解するために開発者からのヒントが必要です。 一部の API では null チェックが提供され、変数の "null 状態" が "null の可能性あり" から "null 以外" に変更されます。 他の API は、入力引数の "null 状態" に応じて、"null 以外" または "null の可能性あり" の式を返します。 たとえば、大文字でメッセージを表示する次のようなコードについて考えます。

void PrintMessageUpper(string? message)
{
    if (!IsNull(message))
    {
        Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
    }
}

bool IsNull(string? s) => s == null;

検査に基づくと、どの開発者もこのコードを安全と見なし、警告は生成されません。 ただし、コンパイラは IsNull が null チェックを実行することを認識していないため、message を "null の可能性あり" の変数と見なし、message.ToUpper() ステートメントに対して警告を出します。 この警告を修正するには、NotNullWhen 属性を使用します。

bool IsNull([NotNullWhen(false)] string? s) => s == null;

この属性は、IsNullfalse を返した場合、パラメーター s が null ではないことをコンパイラに通知します。 コンパイラは、if (!IsNull(message)) {...} ブロック内にある message の "null 状態" を "null 以外" に変更します。 警告は出ません。

属性は、メンバーの呼び出しに使用されるオブジェクト インスタンスの引数、戻り値、メンバーの null 状態に関する詳細情報を提供します。 各属性の詳細については、null 許容参照属性に関する言語リファレンス記事を参照してください。 .NET 5 の時点では、すべての .NET ランタイム API に注釈が付けられています。 スタティック分析を改善するには、API に注釈を付け、引数と戻り値の "null 状態" に関するセマンティック情報を提供します。

Null 許容変数の注釈

"null 状態" 分析は、ローカル変数に対して堅牢な分析を提供します。 コンパイラには、メンバー変数に関するより多くの情報が必要です。 コンパイラは、メンバーの左角かっこですべてのフィールドの "null 状態" を設定するために、より多くの情報を必要とします。 オブジェクトを初期化するには、アクセス可能な任意のコンストラクターを使用できます。 メンバー フィールドが null に設定されている可能性がある場合、コンパイラは、各メソッドの開始時点でその "null 状態" が必ず "null の可能性あり" になると想定します。

変数が null 許容参照型null 非許容参照型かを宣言できる注釈を使用します。 これらの注釈は、変数の "null 状態" に関する重要なステートメントを作成します。

  • 参照が null になることはない。 null 非許容参照変数の既定の状態は、"null 以外" です。 コンパイラでは最初に変数が null ではないことをチェックしないで逆参照しても安全であることを保証する規則が適用されます。
    • 変数は、null 以外の値に初期化される必要があります。
    • 変数に値 null を割り当てることはできません。 コンパイラは、コードが null にすべきではない変数に "null の可能性あり" の式を割り当てるときに、警告を発行します。
  • 参照は null になることがある。 null 許容参照変数の既定の状態は、"null の可能性あり" です。 コンパイラは、null 参照が正しくチェックされていることを保証する規則を適用します。
    • 変数は、値が null ではないことをコンパイラが保証することができる場合にのみ、逆参照される場合があります。
    • これらの変数は、既定の null 値で初期化される場合があり、他のコード内で null 値を割り当てられる場合があります。
    • コードで、null になる可能性がある変数に "null の可能性あり" の式を割り当てる場合、コンパイラは警告を出しません。

null 非許容参照変数の既定の "null 状態" は、"null 以外" です。 Null 許容参照変数の初期 "null 状態" は、"null の可能性あり" です。

null 許容参照型は、null 許容値型と同じ構文を使用して記述し、変数の型の後に ? を追加します。 たとえば、次の変数宣言は、null 許容型の文字列変数 name を表します。

string? name;

Null 許容参照型が有効な場合、その型名に ? が追加されていない変数は、Null 非許容参照型です。 この機能を有効にすると、既存のコード内のすべての参照型変数がそれに含まれます。 ただし、暗黙的に型指定されたローカル変数 (var を使用して宣言) は、null 許容参照型です。 前のセクションで示した通り、スタティック分析によってローカル変数の "null 状態" が判断され、それが逆参照される前に "null の可能性あり" かどうかが判断されます。

場合によっては、変数が null ではないとわかっているときに警告をオーバーライドする必要がありますが、コンパイラはその "null 状態" が "null の可能性あり" と判断します。 変数名の後に null 免除演算子! を使用して "null 状態" を強制的に "null 以外" にします。 たとえば、変数 namenull ではないことがわかっているのに、コンパイラで警告が出る場合は、次のようにコードを記述することで、コンパイラの分析をオーバーライドできます。

name!.Length;

Null 許容参照型と Null 許容値の型は、同様のセマンティック概念を提供します。つまり、変数は値またはオブジェクトを表す、もしくはその変数は null になる場合があります。 ただし、Null 許容参照型と Null 許容値型は異なる方法で実装されます。Null 許容値型は System.Nullable<T> を使用して実装され、Null 許容参照型はコンパイラによって読み取られる属性によって実装されます。 たとえば、string?string は両方とも、同じ型 System.String で表されます。 ただし、int?int はそれぞれ System.Nullable<System.Int32>System.Int32 で表されます。

null 許容参照型はコンパイル時機能です。 つまり、呼び出し元は警告を無視し、null 非許容参照を必要とするメソッドの引数として null を意図的に使用できます。 ライブラリの作成者は、null 引数値に対するランタイム チェックを含める必要があります。 実行時にパラメーターを null に対してチェックする場合は、ArgumentNullException.ThrowIfNull オプションを使用することをお勧めします。

重要

Null 許容注釈を有効にすると、Entity Framework Core でデータ メンバーが必要かどうかを判断する方法が変わる可能性があります。 詳細については、Entity Framework Core の基礎: 「Null 許容参照型の使用」の記事を参照してください。

ジェネリック

ジェネリックには、任意の型パラメーター TT? を処理するための詳細な規則が必要です。 これまでの経緯や、null 許容値型と null 許容参照型では実装が異なるため、規則は必然的に詳細になります。 null 許容値型は、System.Nullable<T> 構造体を使用して実装されます。 null 許容参照型は、コンパイラにセマンティック ルールを提供する型の注釈として実装されます。

  • T の型引数が参照型である場合、T? は、対応する null 許容参照型を参照します。 たとえば、Tstring の場合、T?string? です。
  • T の型引数が値型である場合、T? は同じ値型 T を参照します。 たとえば、Tint の場合、T?int です。
  • T の型引数が null 許容参照型である場合、T? はその同じ null 許容参照型を参照します。 たとえば、Tstring? の場合、T?string? です。
  • T の型引数が null 許容値型である場合、T? はその同じ null 許容値型を参照します。 たとえば、Tint? の場合、T?int? です。

戻り値の場合、T?[MaybeNull]T に相当します。引数値の場合、T?[AllowNull]T に相当します。 詳細については、言語リファレンスの null 状態の分析のための属性に関する記事をご覧ください。

制約を使用して、さまざまな動作を指定できます。

  • class 制約は、T が null 非許容参照型 (例: string) である必要があることを意味します。 Tstring? のような null 許容型参照を使用すると、コンパイラによって警告が生成されます。
  • class? 制約は、T が null 非許容 (string) または null 許容参照型 (例: string?) のどちらかの参照型である必要があることを意味します。 型パラメーターが null 許容参照型 (string? など) である場合、T? の式は、その同じ null 許容参照型 (string? など) を参照します。
  • notnull 制約は、T が null 非許容参照型または null 非許容値型である必要があることを意味します。 型パラメーターに null 許容参照型または null 許容値型を使用すると、コンパイラによって警告が生成されます。 さらに、T が値型の場合、戻り値はその値型であり、対応する null 許容値型ではありません。

これらの制約は、T の使用方法に関するより多くの情報をコンパイラに提供するのに役立ちます。 これは、開発者が T の型を選択する際に役立ち、ジェネリック型のインスタンスが使用される場合に、より適切な "null 状態" 分析を提供します。

null 許容コンテキスト

小規模なプロジェクトの場合は、Null 許容参照型を有効にし、警告を修正して続行することができます。 ただし、大規模なプロジェクトや複数プロジェクト ソリューションの場合は、多数の警告が生成される場合があります。 Null 許容参照型の使用を開始する際に、pragma を使用してファイルごとに Null 許容参照型を有効にすることができます。 System.NullReferenceException をスローから保護する新機能は、既存のコードベースで有効にすると、中断を伴う可能性があります。

  • 明示的に型指定された参照変数はすべて、null 非許容参照型として解釈されます。
  • ジェネリック内の class 制約の意味は、null 非許容参照型を意味するように変更されました。
  • これらの新しい規則により、新しい警告が生成されます。

null 許容注釈コンテキストは、コンパイラの動作を決定します。 null 許容注釈コンテキストには、次の 4 つの値があります。

  • disable: このコードは "Null 許容未指定" です。 Disable は、Null 許容参照型が有効になる前の動作と一致します。ただし、新しい構文ではエラーではなく警告が生成されます。
    • Null 許容の警告は無効になっています。
    • 参照型の変数はすべて、null 許容参照型です。
    • null 許容参照型を宣言する ? サフィックスを使用すると警告が生成されます。
    • null 免除演算子 ! は使用できますが、効果がありません。
  • enable: コンパイラで、すべての null 参照分析とすべての言語機能が有効になります。
    • 新しい Null 許容のすべての警告が有効になります。
    • ? サフィックスを使用して、null 許容参照型を宣言できます。
    • ? サフィックスのない参照型変数は、null 非許容参照型です。
    • null 免除演算子は、null に割り当てられる可能性があるという警告を抑制します。
  • "警告": コンパイラは、すべての null 分析を実行し、コードが null を逆参照する可能性がある場合に警告を出力します。
    • 新しい Null 許容のすべての警告が有効になります。
    • null 許容参照型を宣言する ? サフィックスを使用すると警告が生成されます。
    • 参照型の変数はすべて null にすることができます。 ただし、? サフィックスを使用して宣言しない限り、メンバーは、すべてのメソッドが左中かっこで囲まれた "null 以外" の "null 状態" になります。
    • null 免除演算子 ! を使用できます。
  • annotations: コードが null を逆参照する可能性がある場合、または null 非許容変数に null の可能性ありの式を割り当てる場合、コンパイラは警告を出しません。
    • Null 許容の新しい警告はすべて無効になっています。
    • ? サフィックスを使用して、null 許容参照型を宣言できます。
    • ? サフィックスのない参照型変数は、null 非許容参照型です。
    • null 免除演算子 ! は使用できますが、効果がありません。

プロジェクトの null 許容注釈コンテキストと null 許容警告コンテキストは、 .csproj ファイルの <Nullable> 要素を使用して設定することができます。 この要素は、コンパイラによって型の null 値の許容が解釈される方法と、発せられる警告を構成します。 次の表に、許容される値と、指定されるコンテキストの概要を示します。

Context 逆参照の警告 割り当ての警告 参照型 ? サフィックス ! 演算子
disable 無効 無効 すべて Null 許容です 警告を生成します 影響はありません
enable Enabled Enabled ? で宣言されている場合を除き、null 非許容です。 null 許容型を宣言します null 割り当ての可能性に対する警告を抑制します
warnings Enabled 適用なし すべて Null 許容ですが、メソッドの左中かっこにおいては、メンバーは "null 以外" と見なされます 警告を生成します null 割り当ての可能性に対する警告を抑制します
annotations 無効 無効 ? で宣言されている場合を除き、null 非許容です。 null 許容型を宣言します 影響はありません

"無効な" コンテキスト内のコンパイルされたコードの参照型変数は、"null 許容未指定" です。 null リテラルまたは "null の可能性あり" の変数を、"Null 許容未指定" である変数に割り当てることができます。 ただし、"null 許容未指定" 変数の既定の状態は、"null 以外" です。

プロジェクトに最適な設定を選択できます。

  • 診断や新機能に基づいて更新したくないレガシ プロジェクトには、"無効" を選択します。
  • コードが System.NullReferenceException をスローする可能性がある場所を判断するには、warnings を選択します。 コードが変更され、null 非許容参照型が有効になる前に、これらの警告に対処することができます。
  • 警告が有効になる前にデザインの意図を表現するには、"注釈" を選択します。
  • null 参照の例外から保護する新しいプロジェクトとアクティブなプロジェクトには "有効" を選択します。

:

<Nullable>enable</Nullable>

ディレクティブを使用して、ソース コード内の任意の場所にこれらと同じコンテキストを設定することもできます。 これらのディレクティブは、大規模なコードベースを移行している場合に最も役立ちます。

  • #nullable enable: null 許容注釈コンテキストと null 許容警告コンテキストを、有効に設定します。
  • #nullable disable: null 許容注釈コンテキストと null 許容警告コンテキストを、無効に設定します。
  • #nullable restore:null 許容注釈コンテキストと null 許容警告コンテキストを、プロジェクトの設定に戻します。
  • #nullable disable warnings: null 許容警告コンテキストを無効に設定します。
  • #nullable enable warnings: null 許容警告コンテキストを有効に設定します。
  • #nullable restore warnings:null 許容警告コンテキストをプロジェクトの設定に戻します。
  • #nullable disable annotations: null 許容の注釈コンテキストを無効に設定します。
  • #nullable enable annotations: null 許容の注釈コンテキストを有効に設定します。
  • #nullable restore annotations:注釈の警告コンテキストをプロジェクト設定に復元します。

任意のコード行に対して、次のいずれかの組み合わせを設定できます。

警告コンテキスト 注釈コンテキスト vmmblue_2
プロジェクトの既定 プロジェクトの既定 既定
有効化 (enable) 無効化 (disable) 分析の警告の修正
有効化 (enable) プロジェクトの既定 分析の警告の修正
プロジェクトの既定 有効化 (enable) 型の注釈の追加
有効化 (enable) 有効化 (enable) 既に移行されているコード
無効化 (disable) 有効化 (enable) 警告を修正する前にコードに注釈を付ける
無効化 (disable) 無効化 (disable) 移行されたプロジェクトへのレガシ コードの追加
プロジェクトの既定 無効化 (disable) ほとんどない
無効化 (disable) プロジェクトの既定 ほとんどない

これら 9 つの組み合わせにより、コンパイラがコードに生成する診断をきめ細かく制御できます。 更新している任意の領域内で、さらに多くの機能を有効にすることができます。まだ対処することができない追加の警告は表示されません。

重要

グローバルな Null 許容コンテキストは、生成されたコード ファイルに適用されません。 いずれの方法でも、Null 許容コンテキストは、生成済みとしてマークされているすべてのソース ファイルに対して "無効になります"。 これは、生成されたファイル内のどの API にも注釈が付けられないことを意味します。 ファイルが生成済みとしてマークされる方法は 4 つあります。

  1. .editorconfig で、そのファイルに適用されるセクションで generated_code = true を指定します。
  2. ファイルの先頭にあるコメントに <auto-generated> または <auto-generated/> を配置します。 これは、コメント内の任意の行に配置できますが、コメント ブロックはファイル内の最初の要素である必要があります。
  3. ファイル名を TemporaryGeneratedFile_ で開始します
  4. ファイル名の末尾を .designer.cs.generated.cs.g.cs、または .g.i.cs にします。

ジェネレーターは、#nullable プリプロセッサ ディレクティブを使用してオプトインできます。

既定の null 許容注釈および警告コンテキストは無効です。 これは、既存のコードを変更せずにコンパイルできて新しい警告は生成されないことを意味します。 .NET 6 以降では、新しいプロジェクトには、すべてのプロジェクト テンプレートに <Nullable>enable</Nullable> 要素が含まれています。

これらのオプションでは、null 許容参照型を使用するように既存のコードベースを更新するための 2 つの方法が提供されます。

既知の落とし穴

null 許容参照と null の安全性を決定するスタティック分析において、既知の落とし穴となっているのが、参照型を含む配列と構造体です。 どちらの場合においても、警告を生成せずに、null 非許容参照が null に初期化される場合があります。

構造体

null 非許容の参照型を含む構造体により、警告なしで default を割り当てることができます。 次の例を確認してください。

using System;

#nullable enable

public struct Student
{
    public string FirstName;
    public string? MiddleName;
    public string LastName;
}

public static class Program
{
    public static void PrintStudent(Student student)
    {
        Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
        Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
        Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
    }

    public static void Main() => PrintStudent(default);
}

前の例では PrintStudent(default) に警告はありませんが、null 非許容参照型 FirstNameLastName は null です。

もう 1 つの一般的なケースは、ジェネリック構造体を扱う場合です。 次の例を考えてみましょう。

#nullable enable

public struct S<T>
{
    public T Prop { get; set; }
}

public static class Program
{
    public static void Main()
    {
        string s = default(S<string>).Prop;
    }
}

前の例においては、Prop プロパティは実行時には null です。 これは、警告なしで null 非許容文字列に割り当てられます。

配列

配列も null 許容参照型の既知の落とし穴です。 警告が生成されない次の例を考えてみます。

using System;

#nullable enable

public static class Program
{
    public static void Main()
    {
        string[] values = new string[10];
        string s = values[0];
        Console.WriteLine(s.ToUpper());
    }
}

前の例では、null 非許容の文字列が保持され、その要素がすべて null に初期化されることが配列の宣言からわかります。 その後、変数 snull 値が割り当てられます (配列の最初の要素)。 最後に、変数 s が逆参照され、ランタイム例外が発生します。

関連項目