動作確認。
成果物
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