Null 許容参照型 (C# リファレンス)

この記事では、Null 許容参照型について説明します。 また、Null 許容値型を宣言することもできます。

null 許容対応コンテキスト内のコードでは 、null 許容参照型を使用します。 Null 許容参照型、Null スタティック分析の警告、Null 免除演算子は、オプションの言語機能です。 既定では、すべてオフになっています。 プロジェクト レベルで null 許容コンテキスト を制御するには、ビルド設定を使用するか、プラグマを使用してコードで制御します。

C# 言語リファレンスには、C# 言語の最新リリース バージョンが記載されています。 また、今後の言語リリースのパブリック プレビューの機能に関する初期ドキュメントも含まれています。

このドキュメントでは、言語の最後の 3 つのバージョンまたは現在のパブリック プレビューで最初に導入された機能を特定します。

ヒント

C# で機能が初めて導入された時期を確認するには、 C# 言語バージョン履歴に関する記事を参照してください。

重要

すべてのプロジェクト テンプレートで、プロジェクトに対して の null 許容コンテキスト が有効になります。 以前のテンプレートで作成されたプロジェクトにはこの要素は含まれません。これらの機能は、プロジェクト ファイルで有効にするか、プラグマを使用しない限りオフです。

Null 許容認識コンテキストでは、次の規則が使用されます。

  • null 以外の値で T 参照型の変数を初期化する必要があり、 null可能性のある値を割り当てることはありません。
  • T?を使用してnull参照型の変数を初期化するか、nullを割り当てることができますが、逆参照する前にnullに対して確認する必要があります。
  • mのように、T?型の変数m!に null 許容演算子を適用すると、変数は null 以外と見なされます。

コンパイラは、上記の規則を使用して、null 非許容参照型 T と null 許容参照型 T? の区別を適用します。 T 型の変数と T? 型の変数は、同じ .NET 型です。 次の例は、Null 非許容文字列と Null 許容文字列を宣言し、Null 免除演算子を使用して、値を Null 非許容文字列に割り当てます。

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

変数 notNullnullable はどちらも String 型を使用します。 null 非許容型と null 許容型はどちらも同じ型を使用するため、複数の場所で null 許容参照型を使用することはできません。 一般に、null 許容参照型を基底クラスまたは実装インターフェイスとして使用することはできません。 オブジェクトの作成式または型テスト式で null 許容参照型を使用することはできません。 null 許容参照型をメンバー アクセス式の型として使用することはできません。 次の例は、これらのコンストラクトを示します。

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

Null 許容参照とスタティック分析

前のセクションの例は、Null 許容参照型の特性を示しています。 Null 許容参照型は新しいクラスの型ではなく、既存の参照型に対する注釈です。 コンパイラは、これらの注釈を使用して、ユーザーがコード内で発生する可能性のある Null 参照エラーを検出しやすくします。 Null 非許容参照型と Null 参照型の間には、ランタイムの違いはありません。 コンパイラは、Null 非許容参照型のランタイム チェックを追加しません。 この利点は、コンパイル時分析にあります。 コンパイラは、コード内で発生する可能性のある Null エラーの検出と修正に役立つ警告を生成します。 意図を宣言すると、コンパイラは、コードがその意図に違反したときに警告を発行します。

重要

Null 許容参照注釈では動作の変更は発生しませんが、他のライブラリではリフレクションを使用して、null 許容参照型と null 非許容参照型に対して異なるランタイム動作が生成される場合があります。 特に、Entity Framework Core は 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 非許容参照変数に割り当てると、コンパイラ警告が生成されます。 次のクラスは、これらの警告の例を示します。

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

次のスニペットは、このクラスを使用したときにコンパイラが警告を発行する場所を示します。

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

上記の例では、コンパイラのスタティック分析で、参照変数の "null 状態" がどのように判断されるのかを示しています。 コンパイラは、null のチェックと割り当てに関する言語規則を適用して、その分析を通知します。 コンパイラは、メソッドまたはプロパティのセマンティクスについて想定することはできません。 null チェックを実行するメソッドを呼び出した場合、コンパイラでは、それらのメソッドが変数の "null 状態" に影響することを認識することはできません。 API に属性を追加して、引数と戻り値のセマンティクスについてコンパイラに通知できます。 .NET ライブラリの多くの一般的な API には、これらの属性があります。 たとえば、コンパイラは、IsNullOrEmpty を null チェックとして正しく解釈します。 "null 状態" のスタティック分析に適用される属性の詳細については、"Null 許容属性" に関する記事を参照してください。

Null 許容コンテキスト

null 許容コンテキストは、コンパイラが null 許容参照型の注釈を処理する方法と、静的 null 状態分析中に生成される警告を決定します。 ヌラブルコンテキストには、注釈 設定と 警告 設定の 2 つのフラグが含まれています。

既存のプロジェクトでは、注釈警告 設定の両方が既定で無効になっています。 .NET 6 (C# 10) 以降では、new プロジェクトでは、両方のフラグが既定で有効になります。 null 許容コンテキストの 2 つの異なるフラグの理由は、null 許容参照型の導入前の大規模なプロジェクトの移行を容易にするためです。

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

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

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

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

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

Context 逆参照の警告 割り当ての警告 参照のタイプ ? サフィックス ! 演算子
disable Disabled Disabled すべて Null 許容です 警告を生成します 影響はありません
enable 有効 有効 ? で宣言されている場合を除き、null 非許容です。 null 許容型を宣言します 可能性のあるnull割り当てに対する警告を抑制します
warnings 有効 適用なし すべて null 許容ですが、メソッドの左中括弧位置で、メンバーは not-null であるとみなされます。 警告を生成します 可能性のあるnull割り当てに対する警告を抑制します
annotations Disabled Disabled ? で宣言されている場合を除き、null 非許容です。 null 許容型を宣言します 影響はありません

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

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

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

例:

<Nullable>enable</Nullable>

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

  • #nullable enable: 注釈フラグと警告フラグを enable に設定します。
  • #nullable disable: 注釈フラグと警告フラグを無効にするよう 設定します。
  • #nullable restore: 注釈フラグと警告フラグをプロジェクト設定に復元します。
  • #nullable disable warnings: 警告フラグを無効に設定 します
  • #nullable enable warnings: 有効にする警告フラグを設定します。
  • #nullable restore warnings: 警告フラグをプロジェクト設定に復元します。
  • #nullable disable annotations: 無効にする注釈フラグを設定します。
  • #nullable enable annotations: 有効にする注釈フラグを設定します。
  • #nullable restore annotations: 注釈フラグをプロジェクト設定に復元します。

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

警告フラグ 注釈フラグ Use
プロジェクト デフォルト プロジェクト デフォルト デフォルト
有効にする 無効にする 分析警告を修正する
有効にする プロジェクト デフォルト 分析警告を修正する
プロジェクト デフォルト 有効にする 型の注釈の追加
有効にする 有効にする 既に移行されているコード
無効にする 有効にする 警告を修正する前にコードに注釈を付ける
無効にする 無効にする 移行されたプロジェクトへのレガシ コードの追加
プロジェクト デフォルト 無効にする ほとんどない
無効にする プロジェクト デフォルト ほとんどない

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

重要

グローバル null 許容コンテキストは、生成されたコード ファイルには適用されません。 いずれの方法でも、Null 許容コンテキストは、生成済みとしてマークされているすべてのソース ファイルに対して "無効になります"。 この条件は、生成されたファイル内の API にコンパイラが注釈を付けないことを意味します。 コンパイラは、生成されたファイルに対して null 許容警告を生成しません。 ファイルは、次の 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 許容注釈および警告フラグトは disabled です。 この既定値は、既存のコードが変更されず、新しい警告を生成せずにコンパイルされることを意味します。 .NET 6 以降、新しいプロジェクトにはすべてのプロジェクト テンプレートに <Nullable>enable</Nullable> 要素が含まれるので、これらのフラグを enabled に設定します。

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

Null 許容コンテキストの設定

null 許容コンテキストは、2 つの方法で制御できます。 プロジェクト レベルで、 <Nullable>enable</Nullable> プロジェクト設定を追加します。 単一の C# ソース ファイルで、null 許容コンテキストを有効にする #nullable enable プラグマを追加します。 詳細については、 null 許容戦略の設定を参照してください。 .NET 6 より前の新しいプロジェクトでは、既定の <Nullable>disable</Nullable>が使用されます。 .NET 6 以降では、新しいプロジェクトには、プロジェクト ファイルに <Nullable>enable</Nullable> 要素が含まれています。

ジェネリック

T型パラメーターを null 許容の対応T?として使用すると、実際の型引数によって?の解釈方法が決まります。 次の一般的な宣言について考えてみましょう。

public class Box<T>
{
    public T Contents { get; set; }
}

型パラメーターは参照型または値型に対して使用できるため、 T? の意味は呼び出し元が指定する型引数によって異なります。 次の規則では、Tに制約がない場合に解決T?について説明します。

  • 型引数は null 非許容参照型です。 Box<string>の場合、Tstringされ、T?は対応する null 許容参照型であるstring?
  • 型引数は値型です。 Box<int>の場合、Tintされ、T?も同じ値の型intされます。 注釈は、型パラメーターに struct 制約がない限り、値型には影響しません。その場合、 T?Nullable<T> (int?) を意味します。
  • 型引数は既に null 許容です。 Box<string?>の場合、Tstring?され、T?はまだstring?。 "二重に null 許容" 型は取得されません。

制約 によって、許可される型引数が制限されます。 また、 T の使用方法に関するコンパイラの理由も示します。

  • where T : class には null 非許容参照型が必要です。 Box<string> は許可されます。 Box<string?> は警告を生成します。
  • where T : class? では、null 許容参照型または null 非許容参照型のいずれかを使用できます。 Box<string>Box<string?>の両方が許可されます。
  • where T : struct には null 非許容値型が必要です。 Box<int> は許可されます。 Box<int?> ではありません。 この制約では、ジェネリック手段Nullable<T>内のT?Box<int>の場合、T?int?されます。
  • where T : notnull には、null 非許容参照型または値型が必要です。 Box<string>Box<int> が許可されています。 Box<string?> は警告を生成します。
  • where T : BaseType には、 BaseTypeから派生する null 非許容参照型が必要です。 ? (where T : BaseType?) を追加して、null 許容派生型も許可します。

制約は、ジェネリック型パラメーターの使用方法に関するコンパイラの理由に役立ちます。

public static T? FirstOrDefault<T>(IEnumerable<T> source)
{
    foreach (T item in source)
    {
        return item;
    }
    return default;
}

public static void RequireNotNull<T>(T value) where T : notnull
{
    ArgumentNullException.ThrowIfNull(value);
}

public static void Generics()
{
    string? first = FirstOrDefault<string>([]);
    Console.WriteLine(first ?? "<empty>");

    RequireNotNull("not null");
}

C# 言語仕様

詳細については、C# 言語仕様Null 許容参照型に関するセクションを参照してください。

関連項目