null許容参照型の注意点

NetFramework では、null代入可能な値型をNullable<>型によって表します。T?と略記することができます

一方、参照型ではもともとnull代入可能なので、逆にnullが代入できないことを表す型がなく、実行時にNullReferenceExceptionが発生して初めてミスに気付くことが度々あります。

これを解決するためC# 8.0ではnull許容参照型が導入されました。

記法はnull許容値型と同じく、型名の後に?を付けます。

但し、新たに型が追加されたわけではなく、同じ型についてnullが許容されるかどうかを判別する仕組みで動作します。NullReferenceExceptionが発生する可能性があると思われる場所には、コンパイル時に警告が出るようになっています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace NullableReferenceTypeTest
{
class Test { }

class Container<T>
{
public T? Value { get; set; }

public Container(T? value)
{
Value = value;
}
}

class Client
{
public T? GetValue<T>(Container<T?> container)
{
return container.Value;
}

public void GetValueTest()
{
var c1 = new Container<Test>(new Test());
var v1 = GetValue(c1); //警告発生
var c2 = new Container<Test?>(null);
var v2 = GetValue(c2); //警告が発生しない
}
}
}

C# 9.0 では制約がない型引数に ? をつけれるようになりました。

ジェネリックで制約がない型引数を使ってnull許容かどうかにかかわらず使えるクラスを作成する場合、型引数に ? をつけると問題が発生します。

右のコードはこのような場合に余分な警告が発生することを示すものです。
Container はnull許容かどうかにかかわらず使うことを意図したクラスです。
GetValueTest では c1 にnull許容しない型Testを実引数Tに設定しています。
それに対してc2にはnull許容する型Test?を実引数Tに設定しています。
それぞれGetValueで値を取得しているのですが、null許容しない型c1を引数に指定した場合は警告が出ます。

#nullable enableは、以降でnull許容参照型を有効にすることを示しています。
同様に#nullable disableで無効になり、#nullable restoreで以前の設定に戻ります。

対策としては ! をつけて警告を無視させることも考えられます。

しかし、null許容かどうかにかかわらず使えるクラスの型引数にnull許容参照型を指定することは、上記のあいまいさがあるので、当面これに該当する部分はnull許容参照型を無効のままにしようと考えています。

ジェネリックでない型や、ジェネリックの場合でもnullを許容するかどうか用途が決まっている場合には、null許容参照型を使っていこうと思います。