動作確認。
成果物
nullコンテキスト
nullコンテキストには以下4つの状態がありうる。
#nullable | 注釈 | 警告 |
---|---|---|
○ | ○ | |
○ | ☓ | |
☓ | ○ | |
☓ | ☓ |
- 注釈:
annotations
- 警告:
warnings
注釈と警告をそれぞれ個別に設定するには、以下のようなディレクティブを使う。それぞれの用途もあわせて記載する。
設定方法 | コンテキスト | 用途 | |
---|---|---|---|
#nullable | 注釈 | 警告 | |
enable annotations | ○ | − | null対応を明確にコーディングせねば警告する |
enable warnings | − | ○ | 参照型をnull許容型にしたら警告する |
enable | ○ | ○ | null許容型と非許容型を別型として扱う。違反したら警告する |
disable | ☓ | ☓ | C#8.0以前と同じ |
無効にしたければenable
をdisable
にする。また、プロジェクト設定に戻したければenable
をrestore
にする。
コード
EnableAnnotations.cs
注釈と警告のうち、注釈だけを有効にしたコンテキストのコンパイル結果を確認する。
class EnableAnnotations { public void Run() { #nullable disable ; #nullable enable annotations int i1 = null; // error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません int? i2 = null; string s1 = null; string? s2 = null; string s3 = null!; Console.WriteLine($"{i1}, {i2}, {s1}, {s2}, {s3}"); } }
EnableAnnotations.cs(12,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません
EnableWarnings.cs
注釈と警告のうち、警告だけを有効にしたコンテキストのコンパイル結果を確認する。
class EnableWarnings { public void Run() { #nullable disable ; #nullable enable warnings int i1 = null; // error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません int? i2 = null; string s1 = null; string? s2 = null; // warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。 string s3 = null!; Console.WriteLine($"{i1}, {i2}, {s1}, {s2}, {s3}"); } }
EnableWarnings.cs(12,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません EnableWarnings.cs(15,19): warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。
注釈: #nullable enable annotations
注釈コンテキスト(annotations
)が無効(disable
)なら、C#8.0以前と同じ動作である。つまり以下。
- null許容参照の宣言不可
- 参照変数に
null
代入可 - 参照変数を逆参照しても警告なし
- null免除演算子
!
使用不可
注釈コンテキスト(annotations
)が有効(enable
)なら以下。
- 参照型はすべてnull非許容参照である
- null非許容参照は、安全に逆参照できる
- null非許容参照は、
null
を代入できる- 静的分析により逆参照時の値が
null
以外と判明せねば警告する- このときnull免除演算子
!
を使って非nullであると宣言すれば警告を回避できる
- このときnull免除演算子
- 静的分析により逆参照時の値が
つまり、参照におけるnull
対応を明確にコーディングするならenable
にすべきだし、そうでないならdisable
にすべき。
警告: #nullable enable warnings
警告コンテキスト(warnings
)が有効(enable
)なら、参照がnull
かもしれない場合に警告する。これは注釈コンテキストannotations
の有効(enable
)/無効(disable
)に関係なく行う。
null
であるか否かはコンパイラによる静的分析で判断する。コンパイラが「非nullである」と確定できるのは以下2つのいずれかに該当する場合のみ。それ以外はすべて「nullかもしれない」と判断する。
- 変数に null 以外の値が確実に割り当てられている。
- 変数または式は、それを逆参照する前に null かどうかをチェックされている。
警告する場合は以下2つ。
- 「nullかもしれない」状態の変数が逆参照されたとき
- 注釈コンテキストで「nullかもしれない」状態の変数や式を、null非許容参照型に代入したとき
コンパイラ解析の限界
コンパイラによる静的分析には限界がある。たとえばif
文などを用いたコードのロジックでnull
になりえないとしても、それをコンパイラが静的分析で理解することは不可能である。そのときはプログラマがnull免除演算子!
を付与して非nullであると示さねば警告が出たままとなる。
ただし!
は注釈コンテキスト(annotations
)が有効(enable
)でなければ使えない。
と思っていたのだが、以下コードで警告が出ない。どのコンテキストでも。
string? s4 = (new Random().Next(0, 2) == 0) ? null : "A"; // nullかもしれない Console.WriteLine($"{s4}"); Console.WriteLine($"{s4!}"); if (null != s4) { // nullでない Console.WriteLine($"{s4}"); // 逆参照。非nullであることが確実だから警告を消したい。 Console.WriteLine($"{s4!}"); // 逆参照。非nullであることが確実だから警告を消したい。null免除演算子を使う } else { // nullである Console.WriteLine($"{s4}"); // 逆参照。非nullであることが確実だから警告を消したい。 Console.WriteLine($"{s4!}"); // 逆参照。非nullであることが確実だから警告を消したい。null免除演算子を使う }
用途
用途を予想する。
警告コンテキストは、参照がnull
かもしれない箇所を探すときに有意義なコンテキストである。既存コードのnull
対応についてコードリーディングするときにnull
かもしれない箇所をすぐ発見するのに役立つ。
また、annotations
と併用すればnull
かもしれない箇所に!
を付与することで、非null
であることを明示できる。警告をすべて解消すれば、null安全なコードを書けた状態といえるはず。
ユースケース
null安全なコードを書くときのユースケースについて。
既存コードをC#8.0null
対応コードにしたい
- C#8.0以前の古い既存コードがある
- 1をC#8.0以降のnull対応コードにしたい
- まずは
<Nullable>warnings</Nullable>
で参照変数がnull
かもしれない箇所を網羅する <Nullable>enable</Nullable>
でnull許容型を使えるようにする
既存コードはそのままでC#8.0null
対応コードを追加したい
- 追加コードにおいて変数やメソッドが
null
許容・非許容のいずれであるか設計する - 新規追加コード箇所を
#nullable enable
ディレクティブ指定する - コーディングする
- コンパイルする
- 参照型変数が逆参照されている箇所において警告が出る
- コードを読んで逆参照コードより前の時点で非
null
が確定していることを確認する - 逆参照コードにnull免除演算子
!
をつける - すべての警告箇所において非
null
を確定させるコードに置換できたらnull安全と言える
- コードを読んで逆参照コードより前の時点で非
C#8.0において新しいプロジェクトを作成する
- 変数やメソッドが
null
許容・非許容のいずれであるか設計する <Nullable>enable</Nullable>
でnull許容型を使えるようにする- コーディングする
- コンパイルする
- 参照型変数が逆参照されている箇所において警告が出る
- コードを読んで逆参照コードより前の時点で非
null
が確定していることを確認する - 逆参照コードにnull免除演算子
!
をつける - すべての警告箇所において非
null
を確定させるコードに置換できたらnull安全と言える
- コードを読んで逆参照コードより前の時点で非
ベストプラクティス
- C#8.0において新規プロジェクト作成時は
<Nullable>enable</Nullable>
- 古い既存コードをnull対応に更新するなら
<Nullable>
をwarning
、enable
として確認・改修する - 古い既存コードはそのままにnull対応コードを新規追加するなら
#nullable enable
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux