C で Null 許容参照型#Nullable reference types in C#

この機能の目的は次のとおりです。The goal of this feature is to:

  • 開発者は、変数、パラメーター、または参照型の結果が null であるかどうかを表します。Allow developers to express whether a variable, parameter or result of a reference type is intended to be null or not.
  • そのような変数、パラメーター、結果がその意図に従って使用されない場合は、警告を出します。Provide warnings when such variables, parameters and results are not used according to that intent.

意図の表現Expression of intent

言語には既に値T?型の構文が含まれています。The language already contains the T? syntax for value types. この構文を参照型に拡張するのは簡単です。It is straightforward to extend this syntax to reference types.

非アドナクタ参照型Tの意図は、非 null であると想定されます。It is assumed that the intent of an unadorned reference type T is for it to be non-null.

null 許容参照の検査Checking of nullable references

フロー分析は、null 許容参照変数を追跡します。A flow analysis tracks nullable reference variables. 分析で null ではないとみなす場合 (たとえば、チェックや代入の後)、その値は null 以外の参照と見なされます。Where the analysis deems that they would not be null (e.g. after a check or an assignment), their value will be considered a non-null reference.

また、null 許容参照は、開発者が知っている非 NULL の状況x!をフロー分析で確立できない場合に対して、後置演算子 ("damnit" 演算子) を使用して null 以外として明示的に扱うこともできます。A nullable reference can also explicitly be treated as non-null with the postfix x! operator (the "damnit" operator), for when flow analysis cannot establish a non-null situation that the developer knows is there.

それ以外の場合は、null 許容参照が逆参照された場合、または null 以外の型に変換された場合に警告が表示されます。Otherwise, a warning is given if a nullable reference is dereferenced, or is converted to a non-null type.

S[]から から からT?[]から にS?[]変換するときにT[]警告が表示されます。A warning is given when converting from S[] to T?[] and from S?[] to T[].

型パラメーターが共変 ( C<S> C<T?> out) の場合を除き、 から から からC<S?>``C<T>への変換時、および型パラメーターが反in変である場合は を除き 、 から から に変換するときに警告が表示されます 。A warning is given when converting from C<S> to C<T?> except when the type parameter is covariant (out), and when converting from C<S?> to C<T> except when the type parameter is contravariant (in).

型パラメーターにC<T?>NULL 以外の制約がある場合に警告が表示されます。A warning is given on C<T?> if the type parameter has non-null constraints.

非 NULL 参照の検査Checking of non-null references

NULL リテラルが非 NULL 変数に割り当てられている場合、または null 以外のパラメーターとして渡された場合、警告が表示されます。A warning is given if a null literal is assigned to a non-null variable or passed as a non-null parameter.

また、コンストラクターが null 以外の参照フィールドを明示的に初期化しない場合にも警告が表示されます。A warning is also given if a constructor does not explicitly initialize non-null reference fields.

null 以外の参照の配列のすべての要素が初期化されていることを適切に追跡することはできません。We cannot adequately track that all elements of an array of non-null references are initialized. ただし、配列が読み取りまたは渡される前に、新しく作成された配列の要素が割り当てられていない場合は、警告を発行できます。However, we could issue a warning if no element of a newly created array is assigned to before the array is read from or passed on. それはあまりにも騒がしいことなく、一般的なケースを処理する可能性があります。That might handle the common case without being too noisy.

警告を生成するのか、default(T)単にのタイプとして扱われるのかを決める必要がありますT?We need to decide whether default(T) generates a warning, or is simply treated as being of the type T?.

メタデータ表現Metadata representation

NULL 許容表示要素は、属性としてメタデータで表す必要があります。Nullability adornments should be represented in metadata as attributes. つまり、下位レベルのコンパイラは無視します。This means that downlevel compilers will ignore them.

null 許容の注釈のみが含まれているか、または null 以外がアセンブリ内で "オン" であったかも判断する必要があります。We need to decide if only nullable annotations are included, or there's also some indication of whether non-null was "on" in the assembly.

ジェネリックGenerics

型パラメーターTに null 非許容制約がある場合、そのスコープ内では null 非許容として扱われます。If a type parameter T has non-nullable constraints, it is treated as non-nullable within its scope.

型パラメーターが制約されていない場合、または null 許容制約しか持たない場合、状況は少し複雑です。 eitherIf a type parameter is unconstrained or has only nullable constraints, the situation is a little more complex: this means that the corresponding type argument could be either nullable or non-nullable. このような状況で安全に行うことは、型パラメーターを null 許容と null 非許容の両方として扱い、どちらかに違反したときに警告を出す方法です。The safe thing to do in that situation is to treat the type parameter as both nullable and non-nullable, giving warnings when either is violated.

明示的な null 許容参照制約を許可するかどうかを検討する必要があります。It is worth considering whether explicit nullable reference constraints should be allowed. ただし、特定のケース (継承された制約) では、null 許容参照型が暗黙的に制約になることを避けることはできません。Note, however, that we cannot avoid having nullable reference types implicitly be constraints in certain cases (inherited constraints).

制約classが null 以外です。The class constraint is non-null. "null 許容class?参照型" を示す有効な NULL 許容制約にする必要があるかどうかを検討できます。We can consider whether class? should be a valid nullable constraint denoting "nullable reference type".

型の推論Type inference

型の推論では、影響を与える型が null 許容参照型の場合、結果の型は null 許容型である必要があります。In type inference, if a contributing type is a nullable reference type, the resulting type should be nullable. つまり、NULL が伝播されます。In other words, nullness is propagated.

参加式としてのリテラルがnullNULL を提供するかどうかを検討する必要があります。We should consider whether the null literal as a participating expression should contribute nullness. 今日はそうではありません:値型の場合はエラーになりますが、参照型の場合は null がプレーンな型に変換されます。It doesn't today: for value types it leads to an error, whereas for reference types the null successfully converts to the plain type.

string? n = "world";
var x = b ? "Hello" : n; // string?
var y = b ? "Hello" : null; // string? or error
var z = b ? 7 : null; // Error today, could be int?

ヌルガードガイダンスNull guard guidance

機能として、null 許容参照型を使用すると、開発者は意図を表現し、その意図に矛盾がある場合はフロー分析を通じて警告を表示できます。As a feature, nullable reference types allow developers to express their intent, and provide warnings through flow analysis if that intent is contradicted. ヌルガードが必要かどうかについては、一般的な問題があります。There is a common question as to whether or not null guards are necessary.

ヌルガードの例Example of null guard

public void DoWork(Worker worker)
{
    // Guard against worker being null
    if (worker is null)
    {
        throw new ArgumentNullException(nameof(worker));
    }

    // Otherwise use worker argument
}

前の例では、DoWork関数は aWorkerを受け入れ、nullそれが潜在的に防御します。In the previous example, the DoWork function accepts a Worker and guards against it potentially being null. 引数がworkernull場合、DoWork関数はthrowになります。If the worker argument is null, the DoWork function will throw. null 許容参照型では、前の例のコードはWorker、パラメーターが . not nullWith nullable reference types, the code in the previous example makes the intent that the Worker parameter would not be null. 関数がDoWork、NuGet パッケージや共有ライブラリなどのパブリック API であった場合は、null ガードをそのままにしておきます。If the DoWork function was a public API, such as a NuGet package or a shared library - as guidance you should leave null guards in place. パブリック API として、呼び出し元が渡nullさないという保証は、その API を保護することです。As a public API, the only guarantee that a caller isn't passing null is to guard against it.

エクスプレスインテントExpress intent

前の例のより魅力的な使用はWorker、パラメータが可能であることをnull表現するため、null ガードをより適切にすることです。A more compelling use of the previous example is to express that the Worker parameter could be null, thus making the null guard more appropriate. 次の例で null ガードを削除すると、null を逆参照する可能性があることをコンパイラが警告します。If you remove the null guard in the following example, the compiler warns that you may be dereferencing null. いずれにせよ、両方のヌルガードは引き続き有効です。Regardless, both null guards are still valid.

public void DoWork(Worker? worker)
{
    // Guard against worker being null
    if (worker is null)
    {
        throw new ArgumentNullException(nameof(worker));
    }

    // Otherwise use worker argument
}

開発者や開発チームが完全に制御するソース コードなど、パブリックでない API の場合、null 許容参照型を使用すると、開発者が不要であることを保証できる null ガードを安全に削除できます。For non-public APIs, such as source code entirely in control by a developer or dev team - the nullable reference types could allow for the safe removal of null guards where the developers can guarantee it is not necessary. この機能は警告を引き起こす可能性がありますが、実行時にコードが実行される場合はNullReferenceException保証できません。The feature can help with warnings, but it cannot guarantee that at runtime code execution could result in a NullReferenceException.

重大な変更Breaking changes

null 以外の警告は、既存のコードに対する明らかな変更であり、オプトイン メカニズムを伴う必要があります。Non-null warnings are an obvious breaking change on existing code, and should be accompanied with an opt-in mechanism.

明らかに、null 許容型からの警告 (上記のように) は、NULL 許容が暗黙的な特定のシナリオで既存のコードに対する互換性の変化です。Less obviously, warnings from nullable types (as described above) are a breaking change on existing code in certain scenarios where the nullability is implicit:

  • 制約のない型パラメーターは暗黙的に null 可能として扱われるので、パラメーターobjectを割り当てたりアクセスToStringしたりするなど、警告が生成されます。Unconstrained type parameters will be treated as implicitly nullable, so assigning them to object or accessing e.g. ToString will yield warnings.
  • 型の推論で式のnullnull 値が推論される場合、既存のコードから null 許容でない型ではなく null 許容が生成される場合があり、新しい警告が発生する可能性があります。if type inference infers nullness from null expressions, then existing code will sometimes yield nullable rather than non-nullable types, which can lead to new warnings.

したがって、null 許容の警告もオプションである必要があります。So nullable warnings also need to be optional

最後に、既存の API に注釈を追加すると、警告にオプトインしたユーザーがライブラリをアップグレードする際に、この変更が大きな影響を受けます。Finally, adding annotations to an existing API will be a breaking change to users who have opted in to warnings, when they upgrade the library. これもまた、オプトインまたはオプトアウトする能力にメリットがあります。「バグ修正が必要ですが、新しい注釈を扱う準備ができていません」This, too, merits the ability to opt in or out. "I want the bug fixes, but I am not ready to deal with their new annotations"

要約すると、次の項目をオプトイン/オプトアウトできる必要があります。In summary, you need to be able to opt in/out of:

  • Null 許容警告Nullable warnings
  • NULL 以外の警告Non-null warnings
  • 他のファイルの注釈からの警告Warnings from annotations in other files

オプトインの粒度はアナライザのようなモデルを示唆しており、プラグマでコードをオプトインおよびオプトアウトし、重大度レベルをユーザーが選択できます。The granularity of the opt-in suggests an analyzer-like model, where swaths of code can opt in and out with pragmas and severity levels can be chosen by the user. さらに、ライブラリごとのオプション(「フォールアウトを処理する準備ができるまでJSON.NETからの注釈を無視する」)は、コード内で属性として表現できる場合があります。Additionally, per-library options ("ignore the annotations from JSON.NET until I'm ready to deal with the fall out") may be expressible in code as attributes.

オプトイン/トランジション エクスペリエンスの設計は、この機能の成功と有用性にとって非常に重要です。The design of the opt-in/transition experience is crucial to the success and usefulness of this feature. 次の設定を確認する必要があります。We need to make sure that:

  • ユーザーは、必要に応じて、ヌル可能性チェックを徐々に採用できます。Users can adopt nullability checking gradually as they want to
  • ライブラリ作成者は、顧客を壊す恐れなしに NULL 許容注釈を追加できます。Library authors can add nullability annotations without fear of breaking customers
  • それにもかかわらず、「構成の悪夢」の感覚はありませんDespite these, there is not a sense of "configuration nightmare"

調整Tweaks

ローカルに注釈を?使用するのではなく、それらに割り当てられるものに従って使用されているかどうかを観察することを検討できます。We could consider not using the ? annotations on locals, but just observing whether they are used in accordance with what gets assigned to them. 私はこれを好まない。私たちは、人々が自分の意図を一様に表現できるようにすべきだと思います。I don't favor this; I think we should uniformly let people express their intent.

ランタイムの null チェックT! xを自動生成するパラメーターの短縮形を検討できます。We could consider a shorthand T! x on parameters, that auto-generates a runtime null check.

FirstOrDefault``TryGetなどのジェネリック型の特定のパターンでは、特定の状況で明示的に既定値が生成されるため、null 非許容型引数を使用して、若干変な動作があります。Certain patterns on generic types, such as FirstOrDefault or TryGet, have slightly weird behavior with non-nullable type arguments, because they explicitly yield default values in certain situations. 私たちは、これらにより良い対応のためにタイプシステムをニュアンスしようとすることができます。We could try to nuance the type system to accommodate these better. たとえば、型引数が既?に null 許容である場合でも、制約のない型パラメーターを使用できます。For instance, we could allow ? on unconstrained type parameters, even though the type argument could already be nullable. 私はそれが価値があると疑うし、null許容型との相互作用に関連する奇妙さにつながります。I doubt that it is worth it, and it leads to weirdness related to interaction with nullable value types.

null 許容値型Nullable value types

null 許容値型に対して上記のセマンティクスの一部を採用することを検討できます。We could consider adopting some of the above semantics for nullable value types as well.

すでに、単にエラーを与えるのではなく、からint?``(7, null)推論できる型の推論について述べました。We already mentioned type inference, where we could infer int? from (7, null), instead of just giving an error.

もう 1 つの方法として、フロー分析を NULL 許容値型に適用する方法があります。Another opportunity is to apply the flow analysis to nullable value types. 非nullと見なされた場合、実際には、特定の方法(例えば、メンバーアクセス)でnull非許容型として使用することができます。When they are deemed non-null, we could actually allow using as the non-nullable type in certain ways (e.g. member access). バックコンパトの理由から、null 許容値型でに実行できることが優先されるように注意する必要があります。We just have to be careful that the things that you can already do on a nullable value type will be preferred, for back compat reasons.