C#の?.と??の違い|null安全の考え方を初心者向けに解説

#C##null#null条件演算子#null合体演算子#初心者
MAO先生と生徒うみちゃん、生徒りくちゃんが教室でC#のnull安全と条件演算子を学ぶイラスト
MAO先生と生徒たちを紹介

この記事でわかること

  • C#でnullが原因のエラーを減らす考え方
  • ?.?[]が何をしてくれる演算子なのか
  • ????=を使う場面
  • string?のようなnullable reference typesの読み方
  • !を安易に使わない方がよい理由

結論・重要ポイント

  1. ?.は、左側がnullでなければメンバーへアクセスし、nullなら結果をnullにします。
  2. ??は、左側がnullなら右側の代替値を返します。
  3. ??=は、変数やプロパティがnullのときだけ代入します。
  4. string?nullの可能性を型として示し、コンパイラーの警告で見落としを減らします。
  5. !は警告を消すための最後の手段であり、実行時のnullを消してくれるものではありません。

対象読者と前提知識

この記事は、C#を学び始めて、NullReferenceExceptionstring??.??の違いで迷っている初学者向けです。

前提知識は、変数、クラス、プロパティ、メソッド呼び出しの基本が分かる程度で問題ありません。ここでは、顧客情報を扱う小さな例で、nullが入る可能性をどう読むかに絞って説明します。

3人による授業形式の会話

生徒りくちゃん・疑問を感じている

生徒りくちゃん

C#でuser.Name.Lengthのように書いたら、NullReferenceExceptionになることがあると聞きました。これは、userが存在しない可能性があるということなのですか?

MAO先生・説明中

MAO先生

結論から言うと、userまたはNamenullの可能性があります。C#では、値が入っていない状態をnullで表すことがあり、そのままメンバーへアクセスすると実行時エラーになるのです。

生徒うみちゃん・興味深く聞いている

生徒うみちゃん

じゃあ、全部の場所でifを書いてnullチェックすればいいじゃん!それなら安全でしょ?

MAO先生・笑顔

MAO先生

もちろんifは基本です。ただ、短く安全に書ける場面では?.??を使うと読みやすくなります。大切なのは、nullだったときに何をしたいかを先に決めることです。

生徒りくちゃん・興味深く聞いている

生徒りくちゃん

?.は、左側がnullならその先を呼ばずに止める、ということですね!

MAO先生・説明中

MAO先生

その通りです。例えばuser?.Nameは、usernullなら結果もnullです。さらにuser?.Name?.Lengthのように続ければ、途中でnullになった時点で残りを実行しません。

生徒うみちゃん・疑問を感じている

生徒うみちゃん

でも、表示する文字がnullだと困るなら、最後に??未設定みたいな文字を入れればいいってこと?

MAO先生・自信のある表情

MAO先生

よく気づきましたね!??は、左側がnullのときだけ右側の代替値を使います。表示用の名前、既定の件数、空のリストなど、代わりの値を決めたい場面で役立つのです。

生徒りくちゃん・疑問を感じている

生徒りくちゃん

では、string?と書いてある変数は、nullが入るかもしれない変数だと読めばよいのでしょうか?

MAO先生・注意している

MAO先生

はい。nullable reference typesが有効なプロジェクトでは、string?nullの可能性を示します。逆にstringは、基本的にnullではない前提です。ただし、外部入力や古いコードでは、前提が崩れることもあるので注意します。

生徒うみちゃん・自信のある表情

生徒うみちゃん

警告が出たら、最後に!を付ければ消えるんだよね?それで解決じゃん!

MAO先生・困っている

MAO先生

そこは早とちりしやすい点です。!はコンパイラーへ「ここはnullではないと自分で保証します」と伝えるだけで、実行時の値は変えません。根拠なく使うと、後でNullReferenceExceptionになることがあります。

コード・操作手順・具体例

1. まず危ない例を確認する

次は、userまたはNamenullの場合に危険な書き方です。

public sealed class User
{
    public string? Name { get; set; }
}

User? user = null;

int length = user.Name.Length;

この例では、usernullなのにNameへアクセスしようとしています。実際のアプリでは、画面入力、外部API、データベース、設定ファイルなどから取得した値で同じ問題が起きやすくなります。

2. ?.で途中のnullを受け止める

?.を使うと、左側がnullのときにその先のアクセスを行わず、結果をnullにできます。

public sealed class User
{
    public string? Name { get; set; }
}

User? user = null;

int? length = user?.Name?.Length;

user?.Name?.Lengthの結果は、intではなくint?になります。長さが分からない可能性があるためです。

3. ??で代替値を決める

表示や計算でnullのままだと困る場合は、??で代替値を決めます。

public sealed class User
{
    public string? Name { get; set; }
}

User? user = null;

string displayName = user?.Name ?? "未設定";
int nameLength = user?.Name?.Length ?? 0;

この例では、名前が取れない場合に未設定、長さが取れない場合に0を使います。代替値は便利ですが、業務上本当にその値でよいかは確認が必要です。

4. ??=で初回だけ値を入れる

??=は、左側がnullのときだけ右側を代入します。初期化を短く書きたいときに使えます。

List<string>? messages = null;

messages ??= new List<string>();
messages.Add("問い合わせを受け付けました");

messagesがすでに作られていれば、右側のnew List<string>()は使われません。既存の値を残したいときに読みやすい書き方です。

5. nullable reference typesで意図を示す

#nullable enableが有効なコードでは、stringstring?を分けて考えます。

#nullable enable

public sealed class Customer
{
    public string Name { get; set; } = "";
    public string? Memo { get; set; }
}

Customer customer = new Customer
{
    Name = "MAO工房",
    Memo = null
};

string label = customer.Memo ?? "メモなし";

Nameは基本的に必須、Memoは空でもよい、という意図が型から読み取れます。すべてをstring?にすると、どこでnullを許すのかが分かりにくくなります。

6. !は根拠があるときだけ使う

!は、null forgiving operatorと呼ばれます。警告を消せますが、実行時の値を変えるわけではありません。

string? input = GetNameFromForm();

string name = input!;

static string? GetNameFromForm()
{
    return null;
}

この例のinput!は、コンパイラーの警告を抑えるだけです。実際にはnullが返るため、後続処理で問題が起きる可能性があります。入力チェックや??による代替値の方が適切な場面も多いです。

よくある誤解や失敗

?.を使えばnullの問題は全部消える

誤解です。?.は途中のアクセスを止めるだけです。その結果がnullになるため、最後にどう扱うかを決める必要があります。

??で空文字も代替される

??が反応するのはnullだけです。空文字の""や空白文字はnullではありません。空文字も未入力として扱うなら、別途チェックが必要です。

!を付ければ実行時にも安全になる

誤解です。!はコンパイラーへの指示であり、実行時の値を変えません。根拠なく使うと、警告が消えた分だけ問題に気づきにくくなります。

すべての文字列をstring?にすればよい

すべてをstring?にすると、必須項目と任意項目の違いが読みにくくなります。必須ならstring、未設定を許すならstring?のように、業務ルールに合わせて分けます。

Q&A

?.と??は一緒に使えますか?

使えます。user?.Name ?? “未設定”のように書くと、userまたはNamenullのときに代替文字列を使えます。

??=はどんなときに便利ですか?

リスト、辞書、設定オブジェクトなど、必要になった時点で初期化したい変数に向いています。ただし、共有状態やマルチスレッドで扱う値では、別の設計が必要になる場合があります。

nullable reference typesは必ず有効にするべきですか?

新しいC#プロジェクトでは有効にすると、nullの可能性を早く見つけやすくなります。既存プロジェクトへ後から入れる場合は警告が大量に出ることがあるため、範囲を分けて段階的に進める方が安全です。

!は使ってはいけませんか?

禁止ではありません。テストコード、フレームワークが値を必ず入れる場面、事前チェックでnullでないと証明できる場面では使うことがあります。ただし、警告を消すためだけに広く使うのは避けます。

要点まとめ

  • ?.は、左側がnullならその先を呼ばずに結果をnullにする
  • ??は、nullだったときの代替値を決める
  • ??=は、nullだったときだけ代入する
  • string?は、nullが入る可能性を型で示す
  • !は実行時の安全装置ではなく、根拠があるときだけ使う

公式資料・参考資料

※Microsoft Learnの公式資料を2026年7月16日に確認して作成しています。利用中のC#バージョンやプロジェクト設定によって警告の出方が変わるため、実際の開発環境でも確認してください。

C#や業務アプリの安全な実装を相談できます

nullまわりのエラーは、フォーム入力、データベース、外部API、CSV取り込みなど、業務アプリのいろいろな場所で起きます。

MAO工房では、C#や小規模な業務アプリの実装、既存コードの読みやすさ、入力チェックの整理を支援しています。お問い合わせフォームから現在の困りごとをお知らせください。

MAO工房に相談できます

C#や業務アプリの安全な実装を相談できます

nullエラー、入力チェック、既存コードの読みやすさなど、業務アプリで起きやすい不具合を整理し、保守しやすい実装へ近づける支援をします。

  • ✓ nullチェックの方針を整理
  • ✓ 既存コードの読みやすさを改善
  • ✓ 小規模な業務アプリにも対応