概要
Ver. 8.0
C# くらいの世代(1990年代後半~2000年代前半)のプログラミング言語では、 参照型には null が「つきもの」で、不可避なものでした。 (参考: 「null参照問題」。)
ただ、2010年代ともなると、「つきもの」として惰性で null を認めるのはよくないとされています。 C# でも、少なくとも「意図して null を使っているかどうか」を区別できる必要性が生まれました。
そこで C# 8.0 では、以下のような機能を提供することにしました。
- 参照型でも単に型
T
と書くと null を認めない型になる T?
と書くと null を代入できる型になる
C# 7.X の頃と 8.0 で何が変わったかというと、
「参照型でも null を拒否できるようになった」ということになります。
ただ、「T?
と書いたときに null 許容」という方式なのと、値型との対比として、
この機能はnull許容参照型(nullable reference type)と呼びます(略してNRTと言うことも)。
構文的には C# 2.0 からあったnull許容値型と極力そろうように作れています。
ただ、後入りな機能なので、以下のような制約が掛かります。
-
opt-in (オプションを明示しないと有効にならない)方式
T
の意味が変わるので、opt-in にしないと既存のコードがコンパイルできなくなる
-
警告のみ
T
型の変数に null を代入しても警告だけで、エラーにはならない
-
値型と参照型で、
T?
の挙動が違う- 参照型の
T
とT?
はアノテーション※だけの差で、内部的には差がない - 値型の場合は
T?
と書くと実体はNullable<T>
というT
と明確に異なる型になる - 特に、ジェネリクスを使うときに困る
- 参照型の
※ annotation。「単なる注釈」という意味で、この場合は「コンパイラーがソースコード解析するために使うヒントとなる情報」くらいの意味合い。
null許容参照型の有効化
無条件に「参照型でも null を拒否する」としてしまうと、既存の C# コードの挙動を壊します。
using System;
class Program
{
static void Main()
{
// NRT を opt-in した時点で警告が出るようになる
string s = null; // string (非 null)に null を入れちゃダメ
Console.WriteLine(s.Length); // null の可能性があるものを null チェックせずに使っちゃダメ
}
}
警告だから追加してもいいということにはなりません。 警告を残すのは作法的によくないことですし、 なので、C# には「警告をすべてエラー扱いする」というオプションもあります。 警告の追加も破壊的変更の一種になります。
C# は「既存のソースコードがコンパイルできなくなる」というのをかなり慎重に避けている言語なので、null許容参照型は無条件に入れられる機能ではありません。 そのため、明示的な有効化(opt-in)が必要になります。
opt-in/opt-out の仕方は2通りあります。
- ソースコード中の行単位での切り替え …
#nullable
ディレクティブ - プロジェクト全体での切り替え …
Nullable
オプション
ちなみに、C# は本来、オプションでのオン/オフ切り替えなど、
「文法の分岐」に対してもかなり消極的な言語です。
opt-in 方式で T
の意味が変わるnull許容参照型もだいぶ悩んだ末の苦渋の決断で、
それだけnull参照問題が深刻だということです。
おそらく、C# 史上最初で最後の大きな「分岐」になると思われます。
#nullble ディレクティブ
それなりの規模のソースコードを保守している場合、いきなりnull許容参照型を全面的に有効化してしまうと結構大変なことになります。 (筆者の経験的な話で言うと、少なくとも50行に1個くらいは警告が出ます。何万行ものソースコードを持っている場合、とてもじゃないけど直して回れるものではありません。)
そのため、プリプロセッサー的に、書いたその行以降の opt-in/opt-out をする #nullable
ディレクティブが用意されています。
(#pragma warning
と似たような使い方をします。)
Nullble オプション
一方で、これから新規に作成するプログラムの場合、最初から全部null許容参照型を有効化してしまう方がいいでしょう。 そのくらい、null参照問題は避けたいものです。
warnings/annotations
(予定)