null許容参照型を使用すると、参照型の変数や戻り値などの値をnullにできるか否かを明示したり、不適切なnull参照を発見したりできるようになる。
null許容参照型とは、参照型の変数/パラメーター/戻り値などの値をnullにできるか否かを明示したり、Roslynのコードフロー解析機能などを利用して不適切なnull参照を発見したりできるようにする機構。C# 8.0での導入が予定されている。本稿執筆時点(2017年11月27日)では、プレビュー段階のRoslyn拡張機能として提供されている。開発者が意図していないnull参照の発見と問題の解決を促進するために、この機能を使用できる。
null参照は多くのプログラムのバグ、クラッシュなどの温床となってきた。その一方で、参照型の値に対して初期値をnullとすること自体は(値型の値を0で初期化するのと同様に)非常に便利なことであり、また、「値がnullであること」が意味を持つ場合もある。重要なのは「この値(変数、プロパティ、パラメーター、戻り値など)はnullを参照しても構わない」ということを開発者がコード中に明記できることだ。
そこで、C# 8.0ではnull許容参照型と呼ばれる機構が導入される予定となっている。これは「全ての参照型は基本的には非null許容参照型となり、nullを受け入れられる場合にはそのことを?で修飾することで明示する」ようにするものだ(値型における「null許容型」を参照型にまで拡張したものと捉えることができる)。例えば、以下のようなクラスがあるとしよう。
class Foo
{
public string Name;
public string Phone;
}
このようなクラスは従来のC#コードにおいてはnullを許容する。実際、上のコードなら、インスタンス生成時に2つのフィールドはnullに初期化される。その一方で「フィールド は割り当てられません。常に既定値 null を使用します」といった警告も表示される。以下にVisual Studio 2017(以下、VS 2017)での例を示す。
null参照許容型が有効な場合、上記クラスの2つのフィールドはどちらも非null許容参照型となる。つまり、「public string Name;」などと記述した場合、従来のコードではnullが許容されていたが、null参照許容型が有効な場合、これらのフィールドの値をnullとすることは許されず、警告が発生するようになる(この変更は既存のコードに対する破壊的変更となるので、何らかのオプトイン機構を使ってこれを有効にする、あくまでも警告を発するだけのものにするなどの利用法が考えられている)。
以下はVS 2017 15.5 Preview 4に「null許容参照型」機構をインストールした場合の表示だ。上で見た「既定値のnullを使用する」という旨のメッセージ(英語)に加えて、「Non-nullable field 'Name' is uninitialized」(非null許容参照型のフィールドNameが未初期化)のような警告が表示されていることが分かる。
「非null許容参照型のフィールドNameが未初期化」という警告から分かるように、この場合、2つのフィールドは非null許容参照型として扱われ、nullではない値での初期化が必要となる。[エラー一覧]ウィンドウにも同様なメッセージが表示される。
nullを許容するには、型名に?を付加して、変数やプロパティ、フィールド、戻り値型などをnull許容参照型にする。以下に例を示す。
class Foo
{
public string Name;
public string? Phone; // null許容参照型
}
なお、既に述べた通り、この機能は現在プレビュー段階にあり、VS 2017 15.5 Preview 4以降にインストーラーを利用してインストールしなければ使えない。インストールの手順と注意点については「Nullable Reference Types Preview」ページを参照のこと。
ただし、上記ページのインストール手順の下にはいろいろと注意点が書かれているので、インストールして実際に試してみようという方はよく目を通して、自己責任でインストールすること。例えば、「replacing the existing Roslyn extensions」(既存のRoslyn拡張機能を上書きする)や、「Installing on a machine with multiple versions of Visual Studio is not supported.」(複数のVSが入っているマシンへのインストールはサポートされていない)などの注意点がある。
null許容参照型ではコードのフローを解析しながら、主に以下の2点についてチェックをしてくれる。
前者については、例えば、null許容参照型が使われている部分で、コードフローの解析によりそれがnullでないと判断できれば、それを非null許容参照型のように扱ってくれる(メンバの参照などが自由に行える)。逆にnullチェックなどを行っていないことから、コードフローの解析でnull参照をしていないと判断できないときには警告が表示される。以下に例を示す。
static void Bar(string? s)
{
Console.WriteLine($"length of parameter s: {s.Length}");
}
Barメソッドのパラメーターはnull許容参照型の「string?」型となっている。つまり、このメソッドにはnullが渡される可能性がある。にもかかわらず、nullチェックをしていない。そのため、VS 2017でnull許容参照型を有効にしていると、次のように警告が表示される。
そこで、nullチェックを行うようにコードを修正する。
static void Bar(string? s)
{
if (s != null)
Console.WriteLine($"length of parameter s: {s.Length}");
}
コードフロー解析により、「s.Length」としている箇所では、パラメーターsがnull参照ではないことが判明するので、問題なくLengthプロパティを参照できるようになる。
あるいは、開発者が「これはnull参照ではない」ことを明示することもできる。これには「!」で参照を行っている箇所を修飾する。もちろん、これを行うのは、Roslynによるコード解析では適切な判断が行えないが、開発者が「これはnull参照ではない」ことを担保できる場合に限る。
static void Bar(string? s)
{
Console.WriteLine($"length of parameter s: {s!.Length}");
}
「コードフローの静的解析と開発者自身による注釈によってnull許容参照型がエラーを発生しないこと」が確約されない場合には、上で見たようにnull許容参照型を使用している部分で警告が行われる。
一方、非null許容参照型のチェックとは、nullとなることを許容していない部分でnullの代入が行われていないかをチェックしてくれる機能だ。例えば、上記Barメソッドのパラメーターの型をstring?型からstring型に変更したとする。
static void Bar(string s)
{
Console.WriteLine($"length of parameter s: {s.Length}");
}
static void Main(string[] args)
{
Bar(null); // 非null許容参照型にnullを渡す
Bar("insider.net");
}
このようにすると、(null許容参照型が有効な環境においては)Barメソッドのパラメーターがnullを参照していないことが(ロジックの上では)保証されるので、nullチェックをせずに「s.Length」のように記述しても問題なく扱われる。その一方で、このメソッドにnullを渡そうとすると、その旨が警告されるようになる。
また、先ほども見たが、クラスに非null許容参照型のフィールドがある場合、その初期化を適切に行っていないと警告が表示される。以下に例を示す。これは最初に見たクラスFooに対して、デフォルトコンストラクタを作成したが、まだ初期化コードを記述していない状態のものだ。
注意してほしいのは、string[]などのようにこれまでの「参照型を要素とする配列」だ。「string[] array = new string[100];」のようにして作成した場合、それらの要素は全てnullで初期化される。こうしたコードは無数に存在するであろうこと、対処が難しいことから、(少なくとも現状では)警告が表示されないようになっている。
なお、どのようなときに警告が表示されるかについては、「Nullable reference types in C#」ページや「Introducing Nullable Reference Types in C#」ページを参照されたい。
null許容参照型を使用すると、参照型の変数やパラメーター、プロパティ、戻り値などに対して、「nullを許容するかどうか」を明示的に指定できるようになる。加えて、Roslynを利用したコードフローの解析結果と開発者自身による注釈などを利用することで、プログラムコード中でのnull参照の利用をあぶり出したり、null参照が適切に利用されていることを明記したりできるようになる。ただし、従来の参照型は非null許容参照型として扱われるようになる点には注意が必要だ。
Copyright© Digital Advantage Corp. All Rights Reserved.