NULL許容値型とNULL許容参照型

基本文法

NULL許容値型

最近まではNULL許容型と言っていた筈ですが、いつの間にやらNULL許容値型と呼称が変わってました。
これはC#8.0で追加されたNULL許容参照型の影響なのでしょう。


参照型の変数を宣言した場合、その値はNULLになります。
英語では成ると発音しますが、日本では塗ると発音するのが一般的なようです。
一方、構造体や列挙体など値型の変数を宣言した場合、その値はデフォルト値になります。
例えば数値型の構造体の場合は一般的にがデフォルト値となります。

ここで問題となるのが2つあって、一つは参照型と値型とを同等に扱えないことと、もう一つは値型のデフォルト値が、単なるデフォルト値なのか意図的に設定された値なのかの判断が付かないこと。
そのため、値型でデフォルト値をNULLとして扱えるようにするのがNULL許容値型でありSystem.Nullable構造体のインスタンスを指します。ここでTには値型(構造体)を指定して使います。?という略記も可能です。
英語読みなら塗らぶるですが、日本では塗る等ぶると発音するのが一般的なようです。

メンバーとして主に以下の3つを使います。
HasValue  値が設定されているならTrue、NullならFalse
Value    値が設定されている場合はその値
GetValueOrDefault() 値が設定されている場合はその値が、NULLならデフォルト値を返すメソッド 
// 宣言は2通り可能
System.Nullable<int> number1 = null;
int? number2 = null;
// 値が設定されているかの確認
if(number1.HasValue)
{
    // 設定されている場合はその値を参照可能
    int temp1 = number1.Value;
}

// 値の設定有無はnullとの比較でも可能
if(number2 != null)
{
    var temp2 = number2.Value;
}
// 以下の場合number1がnullなのでintのデフォルト値0が戻値
int temp3 = number1.GetValueOrDefault();

NullableとTとの代入関係


            // Nullable<T> から T;
            int? a = 10;
            int b;

            // このままでは型が違うのでだめ
            //b = a;

            // 明示的なキャストでOK
            b = (int)a;

            // こういうのもOK a がnullの場合は-1が入る
            b = a ?? -1;

            // Nullable<T>のメソッドでもOK
            b = a.GetValueOrDefault();


            // T から Nullable<T>
            int a = 10;
            int? b;

            // そのまま行ける
            b = a;
  

is演算子の実行結果

宣言ではなく実際の値に依拠する
int? number6;
 
number6 = null;
bool ret1 = number6 is int;   // False
bool ret2 = number6 is int?;  // False
 
number6 = 0;
bool ret3 = number6 is int;   // True
bool ret4 = number6 is int?;  // True
 
bool ret5 = 0 is int;         // True
bool ret6 = 0 is int?;        // True

NULL許容参照型

※これはC#8.0で追加された文法なのでそれ以前は使えません

参照型の変数の値が、new演算子でインスタンス化した実態への参照を設定するまでは、nullであることは当然ですし、
値型もまたそれと同等に扱いたいというのは充分に納得できます。
これに対してパラダイムシフトというぐらいに影響ある言語仕様が追加されたました。
参照型のデフォルト値はNULLである必要がないとのこと。NULL許容値型と同等に必要な場合のみ明示的に記述しようという考え方ですね。
言われてみればなるほどです。逐一NULLチェックをしたり、それを回避するための拡張メソッドを定義するより遥かにマシな解決策です。

Null 許容認識コンテキスト

この変更は既存コードへの影響が大きいことから明示的な有効化を行った場合のみ使えるようになっています。

コンパイルオプションによる指定

コード上でプリプロセッサ#nullableによりenable以降(#nullable disableまで)の範囲で有効化される


#nullable enable
            // 警告が出る
            string s = null;
#nullable disable
            string s = null;


プロジェクト全体に設定するにはプロジェクトファイルを書き換える(プロパティで変更する)

サンプルコード

         // 警告が出る
            string s = null;

            // NULL許容値型と同様に?を付与する。デフォルト値(つまりNULL)を設定する
            string? a = default;

            // NULL許容参照型変数のお尻に!を付与するとNULL許容とは見做されなくなる。
            // Null免除演算子というらしい。
            string? b = a!;

            AAA(a); // これは警告がでる
            AAA(b); // これは警告が免除される

            // 引数としてNullableではない文字列を指定
            void  AAA (string s) => Console.WriteLine(s);