C# 8.0 の目玉機能「null許容参照型」で既存プログラムを安全にnull安全にする

# はじめに
C# 8.0は、本稿執筆時点(2019年5月)では、まだプレビュー段階であり、正式リリースされていませんが、プレビュー段階でも利用できます。
本稿は、C# 8.0の目玉機能「null許容参照型」を用いて既存プログラムを安全にnull安全にする方法の紹介です。
なお、既存プログラムでなく、新規でプログラムを作成する際は、無条件でこの機能を有効にした方が良いと思います。実際、私のプロジェクトでは、新規のプログラムは、必ずこの機能を有効にして開発しています。

# null許容参照型とは
従来、値型は int? のように末尾に「?」を記載するかどうかでnullの許容と非許容を区別できましたが、参照型はすべてnull許容でした。
C# 8.0でnull許容参照型の機能を有効にすると、参照型に対して T? と書くとnull許容になり、単に T と書くとnullを認めない型になります。

```c#
    // [object]はnullを設定できないため、以下は警告になる
    object objA = null;

    // [object?]はnullを設定可能なため、以下は警告にならない
    object? objB = null;
```
nullに設定できない型に、nullを設定しようとするコードがあると、ビルド時に表示される[エラー一覧]ウィンドウに警告として表示されます。

この機能の何が嬉しいかというと、想定外の所で変数がnullになって不具合になってしまうのを防いだり、nullの可能性を考慮して毎回null判定するコードを書く苦労を軽減したります。
例えば、以下のようなnull判定のコードは記載不要になります。

```c#
public void Method(object obj)
{
    if (obj == null) 
    {
        // nullの場合になんらか処理する
    }

    ....
}
```

最近はこういうnull安全(英語ではnull safety)と言われる対応が各言語で進んでいます。null安全は、安全性を高めると共に、生産性も高めるため、技術者の関心も高く、以下の記事には[いいね]が1600以上ついています。
[null安全でない言語は、もはやレガシー言語だ](https://qiita.com/koher/items/e4835bd429b88809ab33#null-%E5%AE%89%E5%85%A8%E3%81%AF%E7%94%9F%E7%94%A3%E6%80%A7%E3%82%82%E9%AB%98%E3%82%81%E3%82%8B)

ということで、この機能はぜひ使いましょう。

# null許容参照型の利用方法
まず前提としてC# 8.0を利用するには、Visual Studio 2019が必要で、対象プロジェクトのファイル(.csproj)のLangVersion(C#の言語バージョン)を 8.0 にする必要があります。.csprojのファイルをテキストエディタで開いて編集します。詳細手順は下記を参照ください。
[言語バージョンの指定 | ++C++;](https://ufcpp.net/study/csharp/cheatsheet/langversionoption/)

上記を設定した上で、null許容参照型をプロジェクト全体で有効にするには、NullableContextOptions要素をenableにします。

```xml
プロジェクトのファイル(.csproj)の設定例
<PropertyGroup>
    <LangVersion>8.0</LangVersion>
    <NullableContextOptions>enable</NullableContextOptions>
</Project>
```
ソースコードの行単位で有効にする場合は、#nullable enable と記載すれば、それ以下のコードで有効になります。#nullable disable と記載すれば、それ以下のコードで無効になります。

```C#
#nullable enable // ここ以下の行はnull許容参照型が有効になる
    object? objA = null;

#nullable disable // ここ以下の行はnull許容参照型が無効になる(従来通り)
    object objB = null;
```

上記はVisual Studio 2019のバージョン16.1時点の情報です。プロジェクト全体で有効にする要素名は、16.2で再び変わるようです(要素名はNullableReferenceTypes→NullableContextOptions→nullableと変わってます)。最新の要素名は下記を参照ください。
[null 許容参照型 | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/nullable-references)

上記のバージョンでは、プロジェクト全体で有効にすると、.NET Core と.NET Standardのプロジェクトでは正常に動作しますが、.NET Frameworkのプロジェクトでは適切に警告が表示されませんでした。Visual Studio 2019が正式リリースする前(2019年2月)の記事で、以下のような記載を見つけましたが、まだ.NET Frameworkのプロジェクトは対応途中なのかもしれません。
[プロジェクトをC# 8とnull許容参照型に対応させる | InfoQ](https://www.infoq.com/jp/articles/csharp-nullable-reference-case-study/)
>現在，null許容参照型が使用できるのは、.NET Standardプロジェクトと.NET Coreプロジェクトのみですが，Visual Studio 2019が製品化されるまでには.NET Frameworkでもサポートされるものと思われます。

ただ、.NET Frameworkのプロジェクトでも、各ファイルの先頭に #nullable enable と記載すれば、正常に警告表示されました。少し面倒ですが、現行バージョンでもその方法で利用可能です。
[ファイルの先頭行に文字列を挿入する方法の検索結果](https://www.google.com/search?ei=yDrtXJfCIrrdmAWxtLbICg&q=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB+%E5%85%88%E9%A0%AD+%E6%96%87%E5%AD%97%E5%88%97+%E6%8C%BF%E5%85%A5&oq=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB+%E5%85%88%E9%A0%AD+%E6%96%87%E5%AD%97%E5%88%97+%E6%8C%BF%E5%85%A5&gs_l=psy-ab.3...5488.9133..9602...0.0..0.109.1164.8j4......0....1..gws-wiz.......35i39j0i5i30.74ThMlECvwE)

# 既存プログラムでnull許容参照型を有効にする
既存のプログラムで、この機能を有効にすると、警告だらけになります（手近なプログラム3万行で試した所、400件くらい警告がでました）。
警告を解消するのは面倒ですが、今後も保守し続けていくプログラムであれば、機能を有効にしてソースコードを修正した方がよいと思います（その方が品質も生産性も向上するため）。
既存プログラムを修正する場合に、どうやって直せばよいかについては、以下の記事がとても参考になりました。
[プロジェクトをC# 8とnull許容参照型に対応させる | InfoQ](https://www.infoq.com/jp/articles/csharp-nullable-reference-case-study/)
[C# 8.0 null許容参照型 | ++C++;](https://ufcpp.net/blog/2018/12/cs8nrt/)

# 既存プログラムを安全にnull安全にする
そして本題です。
既存プログラムをそんな容易に変更できないプロジェクトも多いと思います。
例えば、ソースコードが数十万行あって、品質のリスク(デグレードのリスク)を負えないため、ソースコードは極力変更したくない場合もあると思います（実際、私のプロジェクトはそんな感じです）。

折衷案として、新規クラスだけ、#nullable enable を先頭に付けて開発するという方法もありますが、同じプロジェクト内のソースコードの中で、機能が有効なクラスと無効なクラスが混在していると、null許容なのか非許容なのかが分かりづらいため、お勧めしません。やるなら、プロジェクト全体(できればソリューション内の全プロジェクト)で統一した方が効果は高いと思います。

そこで、既存プログラム全体を安全にnull安全にする方法が必要になります。
私が薦める方法は、null許容参照型をプログラム全体で有効にした上で、__警告表示された箇所の変数にひたすら 「?」 を付けていくだけ__の方法です。それならば、品質のリスク(デグレードのリスク)は無いからです。
T と T? の違いは警告が表示されるかどうかだけで、プログラムの挙動は変わりません(T 型の変数に null を設定しても例外は発生しません)。実際の挙動が変わらない変更であれば、それでデグレードが発生するリスクはないという論理です。
例えば、警告の内容が「Null非許容フィールドが初期化されていません」という内容の場合、普通はnullでない値に初期化するように修正しますが、それだと処理ロジックが変わって万が一にもデグレードがあるかもしれないため、変数の型に「?」を付けるだけにします。以下に例を示します。

```c#
修正前
    // このフィールドは初期値がnullになるため
    // 「Null非許容フィールドが初期化されていません」と警告されます
    private string message;  

普通の修正方法
    // 普通は以下のようにnull以外の値で初期化し、null非許容にします
    private string message = string.Empty;

安全な修正方法
    // フィールドの型に「?」を付けて、nullを許容したままにします
    private string? message;
```
また、単純に「?」を付けるだけでは警告が解消できない箇所は、#nullable disable を記述して、その部分だけnull許容参照型を無効にします。
例えば、ジェネリック型のところに、値型か参照型かどちらも入る可能性がある場合は、?を付けても警告は解消できないため、以下のように部分的に無効にします。これもプログラムの挙動は変わらないため、デグレードのリスクはありません。

```c#
public class Sample<T>
{
    #nullable disable // T? にしても警告が解消できないためnull許容参照型を無効にする
    public T GetValue()
    {
        ...
    }
    #nullable enable
}
```
上記の方法であれば、変更の前後でプログラムの挙動は変わらないため、安全にnull安全の状態にできます。

上記の方法は「本来null非許容にできる変数がnull許容のままになってしまう」というデメリットがありますが、それでも警告されなかった変数はnull非許容になりますし、その後の新機能開発はnull安全で開発できる状態になるため、やる価値は高いと思います。

また、いくらかやってみて思いましたが、ひたすら「?」を付けるだけの方法は、考えなくても修正できるので速いです。千件程度の警告でも、ひたすら「?」を付けるだけなら1日でできるんじゃないかと思えました（本稿執筆時点では、まだその規模では実施していないため予想です)。

# まとめ
C# 8.0の「null許容参照型」で以下の方法により、既存プログラムを安全にnull安全にする方法を紹介しました。

- 警告表示された箇所の変数にひたすら 「?」 を付ける。
- 上記で解消できない箇所は、#nullable disable で、その部分だけnull許容参照型を無効にする。