「null 許容参照型」と「null 許容(値)型」
参照型と値型
データの入る型(データ型)には大きく分けて、クラスと構造体(struct)があり、クラスに分類されるデータ型を参照型といい、構造体に分類されるデータ型を値型という。
クラス(参照型)
参照型は、それぞれの固有のデータの他に、nullも代入できる。
C# であれば string 型、Unity であれば GameObject 型、自作したクラス等がクラスであり、参照型である。
つまり元々、クラス(参照型)自体は null を代入できるが、それゆえに「Null Reference Exception」が発生してしまう。
そのため最近の C# では、参照型であるクラスも「?」を付けているときだけ null を代入できる機能が追加され、それが、null 許容参照型である。
「string?」のように参照型であるクラスに「?」を付ける事で null 許容参照型となる。
そして、この機能を利用する場合、単に string と宣言した場合には、null が代入できない状態になる。
「クラス(参照型)は、元々 null が代入できる型であるが、それに明示的に『?』を付けた時だけ null を代入できるようにし、それ以外ではクラス(参照型)であっても null を代入できないようにする事で null チェックの処理や、null エラー自体をなくす」というのが、null 許容参照型の趣旨である。
ただし現在(2022年時点)は、これはプレビュー機能であり、採用しても良いが、エラーまみれになる。
なぜなら「GameObject obj;」と記述してある場合、本来なら自動的に null が代入されるが、null 許容参照型からみればルール違反となるためである。
「こういう方法もある」とだけ覚えておくと良いだろう。
構造体(値型)
一方、構造体は値型というデータ型であり、構造体(値型)には null は無いため、構造体(値型)に null を代入しようとするとエラーになる。
つまり「int x = null;」と記述しても代入できないため、エラーになる。
そして、構造体(値型)には null の状態が100%無いため、null エラーは出ない。
しかし、状態によっては enum のように構造体(値型)であっても null を代入して欲しいケースもある。
その場合「?」をつける事で本来は固有のデータしか入らない構造体(値型)に対して、null を代入できるようになる。
要するに「int? x = null;」と記述すれば、本来なら整数しか入らないデータ型である構造体の int 型に null を代入する事が出来るのである。
これを null 許容型(null 許容値型)という。以前は null 許容型といえば値型しかなかったが、C#8.0 以降では参照型でも null 許容が出来るようになり、区別するために両方の表現がある。
まとめ
型の宣言時に、その型が「クラス(参照型)なのか、構造体(値型)なのか」で「?」の持つ役割が「null 許容参照型なのかnull 許容値型なのか」に区別される。
プログラムの本質として「データの型が参照型なのか値型なのか」を理解しておかなければ「?」の部分は正確に把握できないため、コードを書く際に常に「?」の意味や「参照型/値型」を意識すると良いだろう。