C#のはてな2つ「??」とは?null合体演算子の意味・使い方・「?.」との違いを初心者向けに解説
はじめに
C#のコードを読んでいると、??という「はてな2つ」が出てくることがあります。
C#string name = inputName ?? "ゲスト";
初心者の方は、「これは何をしている記号?」「?が2つある理由は?」「?.とは違うの?」と疑問に感じるかもしれません。
C#の??は、null合体演算子と呼ばれる演算子です。簡単にいうと、値がnullだった場合に、代わりの値を使うための書き方です。
この記事では、C#のはてな2つである??について、意味・基本構文・使い方・?.との違い・??=との違いまで、初心者向けにわかりやすく解説します。
1. C#の「??」とは?はてな2つの正体を初心者向けに解説
1-1. 「??」はnull合体演算子と呼ばれる演算子
C#の??は、null合体演算子と呼ばれます。
「null合体」という名前だけを見ると少し難しく感じるかもしれませんが、役割はとてもシンプルです。
??は、左側の値がnullかどうかを確認し、nullであれば右側の値を返します。
C#string result = value ?? "初期値";
このコードでは、valueがnullでなければvalueが使われます。valueがnullなら、代わりに"初期値"が使われます。
1-2. 左辺がnullなら右辺を返すのが基本ルール
??の基本ルールは、次のとおりです。
C#左辺 ?? 右辺
意味は、次のようになります。
C#左辺がnullでなければ左辺を使う
左辺がnullなら右辺を使う
たとえば、次のコードを見てください。
C#string name = null;
string displayName = name ?? "ゲスト";
Console.WriteLine(displayName);
実行結果は次のようになります。
C#ゲスト
nameがnullなので、??の右側にある"ゲスト"が使われます。
1-3. 「はてな2つ」と検索する人が知りたいポイント
「C# はてな2つ」と検索する人が知りたいのは、主に次のような内容です。
??は何の記号なのか、nullとどう関係しているのか、どんな場面で使うのか、?.や?とは何が違うのか、という点です。
C#では、?が付く構文がいくつかあります。
C#int? age;
user?.Name;
name ?? "ゲスト";
value ??= "初期値";
それぞれ意味が違うため、最初は混乱しやすいです。
この記事で扱う??は、nullだった場合の代替値を指定するための演算子だと覚えておきましょう。
1-4. まず覚えるべき基本構文
まずは、次の形を覚えれば大丈夫です。
C#変数 ?? nullだったときの値
具体例は次のとおりです。
C#string message = text ?? "メッセージはありません";
このコードは、次の意味になります。
C#textがnullでなければtextを使う
textがnullなら"メッセージはありません"を使う
つまり、??はnullチェックを短く書くための便利な構文です。
2. null合体演算子「??」の基本的な使い方
2-1. 文字列がnullのときに初期値を設定する例
??は、文字列の初期値を設定するときによく使われます。
C#string userName = null;
string displayName = userName ?? "ゲスト";
Console.WriteLine(displayName);
実行結果は次のとおりです。
C#ゲスト
userNameがnullなので、displayNameには"ゲスト"が入ります。
一方、userNameに値が入っている場合は、その値が使われます。
C#string userName = "田中";
string displayName = userName ?? "ゲスト";
Console.WriteLine(displayName);
実行結果は次のとおりです。
C#田中
userNameはnullではないため、右側の"ゲスト"は使われません。
2-2. 数値やNullable型で「??」を使う例
??は文字列だけでなく、Nullable型にも使えます。
Nullable型とは、nullを入れられる値型のことです。
C#int? score = null;
int displayScore = score ?? 0;
Console.WriteLine(displayScore);
実行結果は次のとおりです。
C#0
通常のintにはnullを入れられません。
C#int score = null; // エラー
しかし、int?のように書くと、nullを入れられるようになります。
C#int? score = null;
そのため、int?と??を組み合わせると、値がない場合の初期値を簡単に設定できます。
2-3. メソッドの戻り値にデフォルト値を入れる例
メソッドの戻り値がnullになる可能性がある場合にも、??は便利です。
C#string GetUserName()
{
return null;
}
string name = GetUserName() ?? "名前未設定";
Console.WriteLine(name);
実行結果は次のとおりです。
C#名前未設定
GetUserName()の戻り値がnullなので、??の右側にある"名前未設定"が使われます。
APIやデータベースから取得した値がnullになる可能性がある場合、このような書き方はよく使われます。
2-4. if文で書いた場合との比較
??を使わずにif文で書くと、次のようになります。
C#string name = null;
string displayName;
if (name != null)
{
displayName = name;
}
else
{
displayName = "ゲスト";
}
同じ処理を??で書くと、次のようになります。
C#string name = null;
string displayName = name ?? "ゲスト";
どちらも意味は同じです。
ただし、??を使うとコードが短くなり、「nullなら代替値を使う」という意図がわかりやすくなります。
2-5. 「??」を使うとコードが短く読みやすくなる理由
??を使うメリットは、単にコードが短くなるだけではありません。
??を見るだけで、「この値はnullになる可能性があり、nullの場合は別の値を使う」とすぐに理解できます。
C#string title = articleTitle ?? "無題";
このコードは、次のような意図を明確に表しています。
C#記事タイトルがnullなら"無題"を使う
nullチェックの目的が単純な場合は、if文よりも??の方が読みやすくなることが多いです。
3. 「??」の具体的なコード例で動きを理解する
3-1. 左辺がnullではない場合の実行結果
まず、左辺がnullではない場合を見てみましょう。
C#string name = "佐藤";
string result = name ?? "ゲスト";
Console.WriteLine(result);
実行結果は次のとおりです。
C#佐藤
nameには"佐藤"という値が入っています。
そのため、??の右側にある"ゲスト"は使われません。
3-2. 左辺がnullの場合の実行結果
次に、左辺がnullの場合です。
C#string name = null;
string result = name ?? "ゲスト";
Console.WriteLine(result);
実行結果は次のとおりです。
C#ゲスト
nameがnullなので、右側の"ゲスト"が使われます。
このように、??は左辺がnullかどうかで結果が変わります。
3-3. 変数・プロパティ・メソッド呼び出しで使う例
??の左辺には、変数だけでなく、プロパティやメソッド呼び出しも指定できます。
変数で使う例です。
C#string name = userName ?? "ゲスト";
プロパティで使う例です。
C#string name = user.Name ?? "名前なし";
メソッド呼び出しで使う例です。
C#string name = GetName() ?? "名前なし";
どの場合も、左側の結果がnullなら右側の値が使われます。
ただし、プロパティを使う場合は、オブジェクト自体がnullでないことに注意が必要です。
C#string name = user.Name ?? "名前なし";
このコードでは、user自体がnullだとNullReferenceExceptionが発生します。
その場合は、後で解説する?.と組み合わせます。
C#string name = user?.Name ?? "名前なし";
3-4. 複数の「??」をつなげて使う例
??は複数つなげて使うこともできます。
C#string name = nickName ?? userName ?? "ゲスト";
このコードは、次のような意味です。
C#nickNameがnullでなければnickNameを使う
nickNameがnullでuserNameがnullでなければuserNameを使う
どちらもnullなら"ゲスト"を使う
具体例を見てみましょう。
C#string nickName = null;
string userName = "山田";
string displayName = nickName ?? userName ?? "ゲスト";
Console.WriteLine(displayName);
実行結果は次のとおりです。
C#山田
nickNameはnullですが、userNameには値があるため、"山田"が使われます。
3-5. 初心者が間違えやすい実行順序
??を複数つなげた場合、左から順番に確認されます。
C#string result = a ?? b ?? c;
この場合、まずaが確認されます。
aがnullでなければ、aが使われます。aがnullなら、次にbが確認されます。bもnullなら、最後にcが使われます。
初心者が間違えやすいのは、「右側が必ず実行される」と思ってしまうことです。
しかし、??では左側がnullでなければ、右側は評価されません。
C#string GetDefaultName()
{
Console.WriteLine("デフォルト名を取得しました");
return "ゲスト";
}
string name = "田中";
string result = name ?? GetDefaultName();
Console.WriteLine(result);
この場合、nameはnullではないため、GetDefaultName()は呼び出されません。
4. C#の「??」と「?.」の違い
4-1. 「?.」はnull条件演算子
C#には、??と似た見た目の?.があります。
?.は、null条件演算子と呼ばれます。
C#string name = user?.Name;
これは、userがnullでなければuser.Nameを取得し、userがnullなら全体の結果をnullにする書き方です。
たとえば、次のようなコードです。
C#User user = null;
string name = user?.Name;
Console.WriteLine(name);
この場合、userがnullでもNullReferenceExceptionは発生しません。
user?.Nameの結果がnullになります。
4-2. 「??」はnullのときの代替値を指定する演算子
一方、??は、左側がnullだった場合に代わりの値を返す演算子です。
C#string name = userName ?? "ゲスト";
つまり、?.は「nullならアクセスしない」、??は「nullなら代替値を使う」という役割です。
両者は似ていますが、目的が違います。
4-3. 「?.」と「??」の役割の違いを表で比較
| 演算子 | 名前 | 主な役割 | 例 |
|---|---|---|---|
?. | null条件演算子 | オブジェクトがnullならアクセスを止める | user?.Name |
?? | null合体演算子 | 左辺がnullなら右辺の値を使う | name ?? "ゲスト" |
?.は、オブジェクトやプロパティに安全にアクセスするために使います。
??は、nullだった場合の代替値を指定するために使います。
4-4. 「?.」と「??」を組み合わせて使う例
実務では、?.と??を組み合わせて使うことがよくあります。
C#string name = user?.Name ?? "ゲスト";
このコードは、次のような意味です。
C#userがnullなら"ゲスト"
userがnullでなくてもNameがnullなら"ゲスト"
user.Nameに値があればその値を使う
通常の書き方にすると、次のような処理です。
C#string name;
if (user != null && user.Name != null)
{
name = user.Name;
}
else
{
name = "ゲスト";
}
?.と??を組み合わせることで、nullチェックとデフォルト値の設定を短く書けます。
4-5. どちらを使えばいいか迷ったときの判断基準
?.と??で迷ったときは、次のように考えるとわかりやすいです。
オブジェクトがnullかもしれない状態で、プロパティやメソッドにアクセスしたい場合は?.を使います。
C#user?.Name
値がnullだったときに、代わりの値を使いたい場合は??を使います。
C#name ?? "ゲスト"
両方必要な場合は、組み合わせます。
C#user?.Name ?? "ゲスト"
5. C#の「??=」とは?「??」との違い
5-1. 「??=」はnull合体代入演算子
C#には、??に似た??=という演算子もあります。
??=は、null合体代入演算子と呼ばれます。
C#name ??= "ゲスト";
これは、nameがnullの場合だけ、"ゲスト"を代入するという意味です。
5-2. nullのときだけ値を代入する仕組み
??=は、左辺がnullのときだけ右辺の値を代入します。
C#string name = null;
name ??= "ゲスト";
Console.WriteLine(name);
実行結果は次のとおりです。
C#ゲスト
nameがnullだったので、"ゲスト"が代入されました。
次に、すでに値が入っている場合を見てみましょう。
C#string name = "田中";
name ??= "ゲスト";
Console.WriteLine(name);
実行結果は次のとおりです。
C#田中
nameはnullではないため、"ゲスト"は代入されません。
5-3. 「x = x ?? value」と「x ??= value」の違い
次の2つは、似た意味になります。
C#x = x ?? value;
C#x ??= value;
どちらも、xがnullならvalueを使うという意味です。
ただし、??=の方が短く、「nullのときだけ代入する」という意図が明確です。
C#settings = settings ?? new Settings();
これは、次のように書けます。
C#settings ??= new Settings();
初期化処理では、??=の方が読みやすいことが多いです。
5-4. 初期化処理で「??=」が便利なケース
??=は、オブジェクトやコレクションの初期化でよく使われます。
C#List<string> names = null;
names ??= new List<string>();
names.Add("田中");
namesがnullの場合だけ、新しいList<string>を作成します。
すでにnamesにリストが入っている場合は、そのリストをそのまま使います。
プロパティの初期化でも便利です。
C#public List<string> Items { get; set; }
public void AddItem(string item)
{
Items ??= new List<string>();
Items.Add(item);
}
このように書くと、Itemsがnullでも安全にAddできます。
5-5. 「??」と「??=」を混同しないための覚え方
??と??=の違いは、代入するかどうかです。
??は、値を返すだけです。
C#string displayName = name ?? "ゲスト";
??=は、左辺の変数に代入します。
C#name ??= "ゲスト";
覚え方としては、=が付いていれば代入すると考えるとわかりやすいです。
??は「nullなら右を使う」、??=は「nullなら右を代入する」と覚えましょう。
6. null合体演算子「??」を使うメリット
6-1. nullチェックのコードを簡潔に書ける
??を使う最大のメリットは、nullチェックを簡潔に書けることです。
C#string name = inputName ?? "ゲスト";
この1行で、inputNameがnullかどうかを確認し、nullなら"ゲスト"を使う処理を書けます。
if文で書くよりも短く、処理の意図もわかりやすくなります。
6-2. デフォルト値の設定が分かりやすくなる
??は、デフォルト値を設定する場面で特に便利です。
C#int pageSize = request.PageSize ?? 20;
このコードを見ると、PageSizeが指定されていない場合は20を使うことがすぐにわかります。
設定値、ユーザー入力、APIレスポンスなど、値がない可能性があるデータに対して、既定値を設定したいときに役立ちます。
6-3. NullReferenceExceptionの回避に役立つ
??は、NullReferenceExceptionの回避にも役立ちます。
ただし、??だけであらゆるNullReferenceExceptionを防げるわけではありません。
たとえば、次のコードは危険です。
C#string name = user.Name ?? "ゲスト";
user.Nameがnullの場合は問題ありませんが、user自体がnullだと例外が発生します。
この場合は、?.と組み合わせます。
C#string name = user?.Name ?? "ゲスト";
このように書くと、userがnullでも安全に処理できます。
6-4. 可読性が上がるケースと下がるケース
??は便利ですが、いつでも使えばよいわけではありません。
単純なnullチェックであれば、??を使うと可読性が上がります。
C#string title = article.Title ?? "無題";
一方で、複数の条件が絡む場合は、??を多用すると読みにくくなることがあります。
C#string result = a ?? b ?? c ?? d ?? e;
このようなコードは短いですが、どの値が優先されるのかを読み取るのに時間がかかる場合があります。
読みやすさを優先するなら、無理に1行にまとめず、if文を使った方がよいケースもあります。
6-5. 実務でよく使われる場面
実務では、??は次のような場面でよく使われます。
設定値がnullの場合に既定値を使う場面、ユーザー名が未設定の場合に"ゲスト"と表示する場面、APIレスポンスに値がない場合に代替値を入れる場面、オブジェクトのプロパティがnullかもしれない場面などです。
C#string displayName = user?.Name ?? "ゲスト";
int pageSize = request.PageSize ?? 20;
string message = response.Message ?? "メッセージはありません";
このように、??は実務のC#コードで頻繁に登場します。
7. 「??」を使うときの注意点
7-1. 左辺に使える型と使えない型
??の左辺には、nullになり得る型を指定します。
代表的なのは、参照型やNullable型です。
C#string name = null;
string result = name ?? "ゲスト";
C#int? score = null;
int result = score ?? 0;
一方、通常の値型はnullにならないため、??の左辺には使えません。
C#int score = 10;
int result = score ?? 0; // エラー
intで??を使いたい場合は、int?にする必要があります。
C#int? score = null;
int result = score ?? 0;
7-2. 右辺の型を合わせる必要がある
??を使うときは、左辺と右辺の型にも注意が必要です。
C#string name = null;
string result = name ?? "ゲスト";
これは問題ありません。左辺も右辺も文字列として扱えるからです。
しかし、次のようなコードはエラーになります。
C#string name = null;
string result = name ?? 100; // エラー
nameは文字列ですが、右辺の100は整数です。
基本的には、左辺がnullだった場合に右辺を代わりに使えるよう、型を合わせる必要があります。
7-3. 空文字とnullは別物である
??を使ううえで特に注意したいのが、空文字とnullは別物という点です。
C#string name = "";
string result = name ?? "ゲスト";
Console.WriteLine(result);
実行結果は、次のようになります。
C#
何も表示されません。
nameは空文字ですが、nullではありません。そのため、??の右辺にある"ゲスト"は使われません。
空文字も代替したい場合は、string.IsNullOrEmptyを使います。
C#string name = "";
string result = string.IsNullOrEmpty(name) ? "ゲスト" : name;
Console.WriteLine(result);
この場合は、空文字でも"ゲスト"が使われます。
7-4. 使いすぎると読みづらくなる
??は便利ですが、使いすぎるとコードが読みにくくなることがあります。
C#string result = a ?? b ?? c ?? d ?? "未設定";
この程度ならまだ読めますが、右辺にメソッド呼び出しや複雑な式が増えると、理解しづらくなります。
C#string result = GetName() ?? GetDefaultName() ?? CreateTemporaryName();
処理が単純であれば問題ありませんが、複雑になる場合は変数に分けたり、if文で書いたりした方が読みやすいです。
7-5. 条件分岐が複雑な場合はif文の方が分かりやすい
??は、あくまで「nullなら代替値を使う」という単純な処理に向いています。
複数の条件を判断したい場合は、if文の方が適しています。
C#if (user == null)
{
name = "ゲスト";
}
else if (!user.IsActive)
{
name = "無効なユーザー";
}
else
{
name = user.Name;
}
このような処理を無理に??で書こうとすると、かえって読みにくくなります。
??は便利な演算子ですが、読みやすさを優先して使い分けることが大切です。
8. 「??」に関するよくある疑問
8-1. 「??」は三項演算子と何が違う?
三項演算子は、条件によって値を切り替える演算子です。
C#string result = condition ? "A" : "B";
??は、左辺がnullかどうかだけを見て値を切り替えます。
C#string result = name ?? "ゲスト";
三項演算子で同じように書くと、次のようになります。
C#string result = name != null ? name : "ゲスト";
つまり、??は「nullチェックに特化した三項演算子のようなもの」と考えると理解しやすいです。
ただし、三項演算子は任意の条件を書けますが、??はnull判定に特化しています。
8-2. 「??」とif文はどちらを使うべき?
単純に「nullなら代替値を使う」だけなら、??を使うと読みやすいです。
C#string name = inputName ?? "ゲスト";
一方で、条件分岐が複雑な場合はif文の方が向いています。
C#if (inputName == null)
{
name = "ゲスト";
}
else if (inputName.Length > 20)
{
name = "長すぎる名前";
}
else
{
name = inputName;
}
判断基準は、コードを読んだ人がすぐに意味を理解できるかどうかです。
短く書けることよりも、わかりやすく書くことを優先しましょう。
8-3. 「??」は空文字にも反応する?
??は空文字には反応しません。
反応するのはnullだけです。
C#string text = "";
string result = text ?? "未入力";
Console.WriteLine(result);
この場合、resultは空文字になります。
空文字も"未入力"にしたい場合は、次のように書きます。
C#string result = string.IsNullOrEmpty(text) ? "未入力" : text;
空白文字も対象にしたい場合は、string.IsNullOrWhiteSpaceを使います。
C#string result = string.IsNullOrWhiteSpace(text) ? "未入力" : text;
8-4. 「??」はboolやintで使える?
通常のboolやintには??を使えません。
C#int count = 0;
int result = count ?? 10; // エラー
これは、通常のintはnullにならないためです。
ただし、Nullable型であれば使えます。
C#int? count = null;
int result = count ?? 10;
boolの場合も同じです。
C#bool? isActive = null;
bool result = isActive ?? false;
bool?やint?のように、?が付いたNullable型であればnullを扱えるため、??を使えます。
8-5. 「??」は古いC#でも使える?
??は、比較的古いバージョンのC#から使える演算子です。
そのため、現在のC#開発では一般的によく使われています。
ただし、??=は??よりも新しい構文です。古いプロジェクトや古い言語バージョンを使っている場合、??=が使えないことがあります。
??は使えるのに??=でエラーになる場合は、プロジェクトのC#言語バージョンを確認しましょう。
9. C#の「??」を実務で使う典型パターン
9-1. 設定値がnullの場合に既定値を使う
設定値がnullの場合に、既定値を使うパターンです。
C#int? timeout = config.TimeoutSeconds;
int actualTimeout = timeout ?? 30;
このコードでは、config.TimeoutSecondsがnullなら30を使います。
設定ファイルや環境変数から値を取得する場合、値が設定されていないことがあります。そのようなときに??を使うと、自然にデフォルト値を指定できます。
9-2. ユーザー入力がnullの場合に代替文字を表示する
ユーザー入力やプロフィール情報がnullになる可能性がある場合にも、??は便利です。
C#string displayName = userName ?? "ゲスト";
画面表示では、nullをそのまま表示するよりも、"未設定"や"ゲスト"のような文字を表示した方が親切です。
C#string profileText = user.ProfileText ?? "プロフィールは未設定です";
このように、表示用の文字列を整える場面でよく使われます。
9-3. APIレスポンスのnull対策に使う
APIから受け取ったデータは、項目によってnullになることがあります。
C#string message = response.Message ?? "メッセージはありません";
APIレスポンスの値をそのまま使うと、後続処理でnullによる問題が発生することがあります。
??を使って代替値を設定しておくと、画面表示やログ出力の処理を安全に書きやすくなります。
C#string errorMessage = apiResult.ErrorMessage ?? "不明なエラーが発生しました";
9-4. オブジェクトのプロパティ参照時に使う
オブジェクトのプロパティがnullになる可能性がある場合にも、??はよく使われます。
C#string title = article.Title ?? "無題";
ただし、オブジェクト自体がnullになる可能性がある場合は、?.と組み合わせます。
C#string title = article?.Title ?? "無題";
このコードであれば、articleがnullでも、article.Titleがnullでも、最終的に"無題"が使われます。
実務では、この組み合わせが非常によく登場します。
9-5. LINQやコレクション処理で使う
LINQやコレクション処理でも、??は役立ちます。
たとえば、コレクションがnullの場合に空のリストとして扱いたい場合です。
C#List<string> names = null;
foreach (var name in names ?? new List<string>())
{
Console.WriteLine(name);
}
namesがnullでも、代わりに空のリストを使うため、foreachで例外が発生しません。
また、LINQの結果がnullになる可能性がある場合にも使えます。
C#var items = GetItems() ?? Enumerable.Empty<string>();
foreach (var item in items)
{
Console.WriteLine(item);
}
このように、??を使うことで、nullの可能性があるコレクションを安全に扱いやすくなります。
まとめ
C#のはてな2つである??は、null合体演算子です。
基本的な意味は、次のとおりです。
C#左辺 ?? 右辺
左辺がnullでなければ左辺を使い、左辺がnullなら右辺を使います。
C#string name = userName ?? "ゲスト";
このコードでは、userNameがnullなら"ゲスト"が使われます。
??は、nullチェックを短く書ける便利な演算子です。文字列、Nullable型、メソッドの戻り値、APIレスポンス、設定値、プロパティ参照など、実務でもさまざまな場面で使われます。
また、?.との違いも重要です。
?.は、nullの可能性があるオブジェクトに安全にアクセスするための演算子です。
C#user?.Name
??は、nullだった場合の代替値を指定する演算子です。
C#name ?? "ゲスト"
両方を組み合わせると、より安全で読みやすいコードになります。
C#string name = user?.Name ?? "ゲスト";
さらに、??=はnullの場合だけ代入する演算子です。
C#name ??= "ゲスト";
??、?.、??=は見た目が似ていますが、役割はそれぞれ違います。
??は「nullなら右を使う」、?.は「nullならアクセスしない」、??=は「nullなら右を代入する」と覚えると理解しやすいです。
C#でnullを扱う場面は非常に多いため、??を理解しておくと、コードを短く、安全に、読みやすく書けるようになります。

