1. NullReferenceExceptionの問題
ほとんどのC#初心者(だけじゃなくて経験者も!)は一度はこの怖いメッセージに出会ったことがあるはず:
System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
これぞ定番のやつ:
string hello = null;
Console.WriteLine(hello.Length); // バン! NullReferenceException
意味はシンプル:存在しないオブジェクトにアクセスしようとしてるってこと。つまり変数がnull、つまりオブジェクトの代わりに空っぽを指してる。
なぜこのエラーは起きやすい?
なぜならC#のほとんどのリファレンスタイプは昔からnullを取れるから。プログラマーは変数が本当にオブジェクトを指してるかチェックし忘れて、結果として有名なNullReferenceExceptionを食らうんだ。
もしプログラマーがNullReferenceExceptionを食らうたびに1ドルもらえたら、とっくに自分のOS作ってるよね。
なぜこれが問題なの?
前にint?やdouble?みたいな値型をnullにできるって学んだよね。これは「値がない」って明示したいとき(例えばDBのフィールドが空とか)に便利。
でもリファレンスタイプ(stringとか、クラス、配列など)は最初からずっとnullを取れた。これは便利だけど危険でもある。なぜなら言語が「この参照は空かも?」って考えることを強制してくれなかったから。
2. 進化:Nullable Reference Types (NRT)
5年前、NullReferenceException対策の考え方を根本から変える新機能、Nullable Reference Types(NRT)が登場したんだ。
string s = "hello"; // not-nullable reference
string? maybe = null; // nullable reference
メインアイデア:
- 絶対にnullにならない参照変数と、nullになってもOKな参照をはっきり分ける。
- プログラマーがnullの問題をランタイムじゃなくてコンパイル時に気付けるようにする。
新しいC#では、リファレンス変数の宣言方法が変わった:デフォルトでnullを代入できない(明示的に許可しない限り)。
たった1文字?を付けるだけで、変数の意味がガラッと変わるんだ!
3. Nullable Reference Typesの有効化
デフォルトではこの厳密な構文はオフになってることが多い。古いコードを壊さないためだね。でも今どきのVisual StudioやRider、.NET CLIのプロジェクトテンプレートはNRTがオンになってるか、少なくとも有効化を強く勧めてくる。
自分のプロジェクトでNRTが有効かどうかは、.csprojファイルでこの行を探してみて:
<Nullable>enable</Nullable>
この行がなければ、自分で追加しても大丈夫だよ。
これがコードにどう影響する?
- NRTがオフ(昔の挙動):string s = null; — エラーなし、誰も気にしない。
- オンの場合:nullをダメなところに入れようとするとコンパイラが怒る。
4. NRTの例
シンプルな例
#nullable enable // この行でこのファイルのNRTチェックを有効化
string notNullable = "こんにちは";
string notNullable2 = null; // コンパイルエラー!
string? nullableString = null; // これはOK、明示的にnullを許可してる
最初の行は、必ず実体を指すstringを宣言してる。
3行目は、nullもOKなstringを宣言してる。
nullチェック
void PrintLength(string? s)
{
// コンパイラが警告するよ: "もしs == nullだったら?"
Console.WriteLine(s.Length);
// こうすればOK
if (s != null)
{
Console.WriteLine(s.Length);
}
}
コンパイラがnullチェック忘れをサポートしてくれる!
コンパイラの警告
警告を無視してnullable変数にチェックなしでアクセスしようとすると、新しい(しかも超ありがたい!)コンパイラ警告が出る。これはエラーじゃない(ビルドは通る)が、黄色い「ランプ」で「本当に大丈夫?」って教えてくれる。
動作モードの比較:
| C#タイプ | nullになれる? | レガシーモード(NRT前) | NRTモード(#nullable enable) |
|---|---|---|---|
| int | いいえ | いいえ | いいえ |
| int? | はい | はい | はい |
| string | はい | いいえ(常にOK) | いいえ(デフォルトはダメ) |
| string? | はい | いいえ | はい |
5. NRTをどう使う?なぜ使う?アドバイス
- バグ減少: nullで落ちることが減って、開発者もユーザーもハッピー。
- コードの明快さ: どこが空になり得るか、どこが絶対値入りか一目瞭然。
- コンパイラのサポート: もう認めよう、コンパイラは本当に助けてくれる!NRT警告は潜在バグの宝庫。
どんなときに必須?
- 大規模プロジェクトで、複数人が同じコードを触るとき。
- APIや公開ライブラリで、他人に「ここはこう使ってね」って伝えたいとき。
- 信頼性が超重要な場面(銀行アプリ、医療システムなど)
6. よくあるミスと落とし穴
- 「?を付け忘れた」
普通のstringにnullを代入(string s = null;)しようとして、コンパイラに怒られる。今はデフォルトで普通のstringはnull禁止だからね。 - 「?を付けすぎた」
とりあえず全部string?にしてコンパイラの警告を消そうとする。でも本当は、どこが空OKかちゃんと考えてアノテーションすべき。 - 「警告の意味を勘違い」
警告を無視して、あとでNullReferenceExceptionを食らう。コンパイラの親切を信じよう!
GO TO FULL VERSION