nullチェックの簡略化
プログラミングにおいて、「変数が null であればデフォルト値を使い、そうでなければ変数の値をそのまま使う」というロジックは頻繁に登場します。
例えば、ユーザーのニックネームが未設定(null)の場合に “Guest” という文字列を表示するようなケースです。これを従来の if-else 文や条件演算子(三項演算子 ?:)で記述すると、コードが冗長になりがちです。
string? userName = null;
string displayName;
// if-else を使った場合
if (userName != null)
{
displayName = userName;
}
else
{
displayName = "Guest";
}
// 条件演算子を使った場合
displayName = (userName != null) ? userName : "Guest";
C#では、このような「nullならこっち」という処理を簡潔に記述するために、null合体演算子 (??) が用意されています。
この記事では、?? 演算子の基本的な使い方と、C# 8.0で導入された null合体割り当て演算子 (??=) について解説します。
null合体演算子 (??) の基本
null合体演算子 ?? は、左側のオペランド(被演算子)が null でない場合はその値を返し、null である場合は右側のオペランドを評価して返します。
構文: 左側の値 ?? 右側の値(デフォルト値)
コード例:設定値の取得
ユーザーの設定ファイルパスを取得するロジックを例にします。カスタム設定パスが指定されていればそれを使い、指定されていなければ(nullならば)デフォルトのパスを使用します。
using System;
public class NullCoalescingExample
{
public static void Main()
{
// ユーザーが指定したパス(未指定の場合は null)
string? userConfigPath = null;
// デフォルトのパス
string defaultConfigPath = @"C:\App\Config\default.json";
// --- null合体演算子 (??) の使用 ---
// userConfigPath が null なので、defaultConfigPath が採用される
string finalPath = userConfigPath ?? defaultConfigPath;
Console.WriteLine($"使用する設定ファイル: {finalPath}");
// --- 値が入っている場合 ---
userConfigPath = @"D:\MyData\custom.json";
// userConfigPath が null ではないので、userConfigPath が採用される
finalPath = userConfigPath ?? defaultConfigPath;
Console.WriteLine($"使用する設定ファイル: {finalPath}");
}
}
出力結果:
使用する設定ファイル: C:\App\Config\default.json
使用する設定ファイル: D:\MyData\custom.json
このように、?? を使用することで、意図が明確になりコードが非常に短くなります。
null合体演算子のチェーン(連鎖)
?? 演算子は連鎖させることができます。複数の変数を順番にチェックし、最初に見つかった「非null」の値を取得する場合に便利です。
string? inputName = null;
string? storedName = null;
string defaultName = "No Name";
// inputName が null なら storedName をチェック
// storedName も null なら defaultName を採用
string displayName = inputName ?? storedName ?? defaultName;
Console.WriteLine(displayName); // "No Name"
null合体割り当て演算子 (??=)
C# 8.0からは、null合体割り当て演算子 (??=) が利用可能です。 これは、「変数が null の場合にのみ、右側の値を代入する」という操作を行います。
構文: 変数 ??= 値;
これは以下のコードと等価です。
if (変数 is null)
{
変数 = 値;
}
コード例:遅延初期化(Lazy Initialization)
リストなどのオブジェクトを使用する直前に、まだ生成されていなければ(nullであれば)生成する、といった初期化処理に最適です。
using System;
using System.Collections.Generic;
public class NullCoalescingAssignmentExample
{
public static void Main()
{
List<string>? errors = null;
// errors が null なので、新しい List<string> を作成して代入する
errors ??= new List<string>();
errors.Add("Error 1: Connection failed.");
Console.WriteLine($"エラー数: {errors.Count}");
// この時点では errors は null ではない (インスタンスが存在する)
// そのため、右側の new List<string>() は評価されず、代入も行われない
errors ??= new List<string>();
errors.Add("Error 2: Timeout.");
Console.WriteLine($"エラー数: {errors.Count}"); // カウントは増えている
}
}
出力結果:
エラー数: 1
エラー数: 2
まとめ
?? および ??= 演算子は、C#におけるnull処理を劇的に簡潔にする機能です。
val ?? default:valがnullのときにdefaultを返します。値の取得や表示に使用します。val ??= newValue:valがnullのときにnewValueを代入します。遅延初期化やデフォルト値の設定に使用します。
これらの演算子を積極的に利用することで、if (x == null) のような定型的な記述を減らし、ビジネスロジックの本質的な部分に集中できるようになります。
