参照型における「null」の問題
C# 8.0より前では、stringやクラスなどの参照型は、デフォルトでnullを許容していました。そのため、開発者が意図せずnullの状態の変数にアクセスしてしまい、実行時にNullReferenceException(ぬるぽ)が発生するというバグが後を絶ちませんでした。
この長年の課題を解決するために導入されたのが「null許容参照型(Nullable Reference Types)」です。
この機能を有効にすると、参照型の変数はデフォルトで「null非許容(Non-Nullable)」として扱われるようになり、nullが入る可能性がある場合は明示的に?を付けて宣言する必要があります。これにより、コンパイラが事前にnullの可能性を検知し、警告を出してくれるようになります。
この記事では、null許容参照型の有効化方法から、具体的なコードでの書き方、警告の抑制方法までを解説します。
プロジェクトでの有効化
.NET 6以降の新しいプロジェクトテンプレートでは、デフォルトでこの機能が有効になっていますが、既存のプロジェクトや明示的に設定を確認する場合は、.csprojファイルを編集します。
<PropertyGroup>セクション内に<Nullable>enable</Nullable>を追加します。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
型の定義:string と string? の違い
機能を有効にすると、参照型の宣言方法が以下の2つに分かれます。
1. 非null許容参照型 (string)
単に string と書いた場合、その変数に null を代入することは許可されません。 初期化せずに使用したり、nullを代入しようとすると、コンパイラが警告を出します。
2. null許容参照型 (string?)
型名の後ろに ? を付けて string? と書いた場合、その変数には null を代入できます。 ただし、その変数にアクセス(.Lengthなど)する際は、事前にnullチェックを行わないと警告が出ます。
コード例:ユーザープロファイルの実装
「表示名(必須)」と「自己紹介(任意)」を持つユーザープロファイルクラスを例に、正しい実装方法を見ていきます。
using System;
public class UserProfile
{
// 1. 非null許容プロパティ (string)
// nullが入ることは想定されていないため、必ず初期値が必要。
// コンストラクタで初期化しないと警告が出る。
public string DisplayName { get; set; }
// 2. null許容プロパティ (string?)
// nullが入る可能性があるため、? を付ける。初期値は null で問題ない。
public string? Bio { get; set; }
// コンストラクタ
public UserProfile(string name)
{
// DisplayName は非nullなので、ここで確実に値を設定する
DisplayName = name;
}
}
public class Program
{
public static void Main()
{
// インスタンス生成
var user = new UserProfile("Alice");
// --- 非null許容型へのアクセス ---
// DisplayName は null ではないことが保証されているため、
// チェックなしで安全にアクセスできる。
Console.WriteLine($"名前の長さ: {user.DisplayName.Length}");
// 警告: 非null許容型に null を代入しようとすると警告が出る
// user.DisplayName = null;
// --- null許容型へのアクセス ---
user.Bio = null; // 代入はOK
// 警告: nullの可能性があるため、直接アクセスすると警告が出る
// Console.WriteLine(user.Bio.Length);
// 解決策1: nullチェックを行う
if (user.Bio != null)
{
Console.WriteLine($"自己紹介の長さ: {user.Bio.Length}");
}
// 解決策2: null条件演算子 (?.) を使う
Console.WriteLine($"自己紹介: {user.Bio ?? "(未設定)"}");
}
}
出力結果:
名前の長さ: 5
自己紹介: (未設定)
null免除演算子 (!)
コンパイラの静的解析は優秀ですが、完璧ではありません。開発者側から見て「ここでは絶対にnullではない」とわかっている場面でも、コンパイラが警告を出してしまうことがあります。
そのような場合、変数の後ろに !(ビックリマーク)を付けることで、「これはnullではないので警告を無視してよい」とコンパイラに指示できます。これを「null免除演算子(Null-forgiving operator)」と呼びます。
コード例:! の使用
using System;
public class NullForgivingExample
{
public static void Main()
{
string? message = GetMessage();
// 論理上は null ではないとわかっているが、
// 型が string? なので通常は警告が出る。
// ! を付けて警告を抑制する。
Console.WriteLine($"メッセージの長さ: {message!.Length}");
}
// 外部要因などで null を返す可能性があるシグネチャだが、
// このテストケースでは確実に文字列を返すと仮定
private static string? GetMessage()
{
return "Hello";
}
}
注意: ! はあくまで「警告を消す」だけであり、実行時の動作を変えるものではありません。もし実際には null だった場合、当然ながら NullReferenceException が発生します。乱用は避け、本当に安全な場合のみ使用すべきです。
まとめ
null許容参照型を有効にすることで、コードの堅牢性が飛躍的に向上します。
- 設定:
.csprojで<Nullable>enable</Nullable>を設定します。 Type:nullを許容しません。初期化が必須です。Type?:nullを許容します。使用時にnullチェックが必須です。var!: どうしても警告を消したい場合は!で抑制します。
これらのルールに従ってコーディングすることで、コンパイル時に多くのnull関連エラーを検出し、実行時のクラッシュを未然に防ぐことができます。
