Skip to main content

第06章:Preの基本:null・空・未指定を守る🚫🕳️

DbCの「Pre(事前条件)」は、**“呼び出し側が守る約束”**だよ〜🤝✨ 特に最初に躓きやすいのが、null / 空 / 未指定の扱い🤯💦 ここを整理すると、入口チェック(ガード節)が一気にラクになるよ🛡️🌸


6.1 まずは3つをハッキリ分けよう🧠✨

同じ「入ってない」でも意味が違うよ👇

  • null:値そのものが無い(参照が無い)🕳️
  • :値はあるけど中身が空(例:""、Count==0)📭
  • 未指定:「わざと入れてない」ことを表したいケース(オプション)🫥

ここで大事なのは… あなたのAPI(publicメソッド)が“どれを許すか”を決めることだよ🚪🔑


6.2 ありがちな事故あるある😵‍💫💥

✅ 事故①:nullと空を混ぜて「なんとなく通す」

  • 例:ユーザー名が "" でも通ってしまい、表示崩れ・検索不能・重複判定バグ…など💣

✅ 事故②:未指定のつもりで null を使って、別の意味に見える

  • 「未指定=デフォルトを使ってね」なのか
  • 「値が壊れてる(契約違反)」なのか が曖昧になって事故るよ〜⚠️

6.3 “許す・許さない”を決めるミニ基準⚖️🌸

迷ったらこのルールが便利👇

ルールA:受け取らないものは入口で弾く🚪🛡️

  • 「nullは受け取らない」→ 入口で即例外
  • 「空は受け取らない」→ 入口で即例外
  • 「空配列は受け取らない」→ 入口で即例外

ルールB:「未指定」を表したいなら、表現を固定する📌

おすすめは2択だよ✨

  • 選択肢1:オプションは nullable(string? など)で表現🫥

    • null = 未指定(意味がある)
  • 選択肢2:オーバーロード / 引数省略で “未指定” を表現🧩

    • 引数を渡さない = 未指定(意味がある)

どっちでもいいけど、1つのAPI内で混ぜないのが超大事だよ💡


6.2 null, "", " " の違い(超重要!)🧱📦

null, 空文字, 空白文字の違い

「未入力」と言っても、C#の世界ではこの3つを区別することが超大事だよ🧠✨


6.4 ガード節の基本形:「上で弾いて下をスッキリ」🛡️📐

入口チェックは、最初にまとめて短くが読みやすい✨ .NET 9〜10あたりでは、ガード用のThrowIf系がかなり便利になってるよ💖

(.NET 10は2025-11-11リリースのLTSで、いま現場の基準になりやすい版だよ📌) (Microsoft)


6.5 文字列のPre:null / 空 / 空白だけ を分ける📛🧻

✅ ケース1:nullはNG、空もNG、空白もNG(いちばん多い)✨

public static void UpdateDisplayName(string displayName)
{
// null / "" / " " を全部NGにする
ArgumentException.ThrowIfNullOrWhiteSpace(displayName);

// ここから下は「displayNameはちゃんと入ってる」世界🌈
// ...
}
  • “null/空/空白”をまとめて弾けるよ💪
  • APIとしても読みやすいし、バグが減る✨ (Microsoft Learn)

✅ ケース2:nullはNGだけど、空文字は「意味がある」なら許す

例:メモ欄は "" を許す(ただしnullは困る)みたいなケース📒

public static void UpdateMemo(string memo)
{
ArgumentNullException.ThrowIfNull(memo);

// memo == "" はOK(仕様として許してる)
}

ArgumentNullException.ThrowIfNull は、引数名の自動取得とも相性が良いよ🧠✨ (Microsoft Learn)

✅ ケース3:nullはNG、空文字だけNG(空白は許す?)

「空白だけはNG」かどうかは、業務によるよ〜🤔 もし「空文字はNG、空白もNG」に寄せたいなら、素直にThrowIfNullOrWhiteSpaceがラクだよ💗 (Microsoft Learn)


6.6 コレクションのPre:nullと空の意味を決める📦🧺

コレクションはこの2パターンが多いよ👇

✅ パターン1:「nullはNG、空はOK」(“0件”が意味を持つ)🧘‍♀️

例:検索フィルタが0個なら「全部対象」など🔍

public static void Search(IReadOnlyList<string> tags)
{
ArgumentNullException.ThrowIfNull(tags);

// tags.Count == 0 はOK(=指定なし)
}

✅ パターン2:「nullも空もNG」(“最低1件は必要”)🔥

例:一括更新の対象IDが0件は困る、など

public static void BulkDelete(IReadOnlyList<int> ids)
{
ArgumentNullException.ThrowIfNull(ids);
if (ids.Count == 0)
throw new ArgumentException("ids must contain at least one element.", nameof(ids));

// ここから下は「最低1件ある」世界✨
}

6.7 “未指定”の表現:nullを使うなら、契約として宣言する🫥📌

たとえば「表示名は未指定なら現状維持」みたいなAPIを作りたい時👇

public static void UpdateProfile(string userId, string? displayName)
{
ArgumentException.ThrowIfNullOrEmpty(userId);

// displayName == null は “未指定” として扱う(意味がある)
if (displayName is null) return;

ArgumentException.ThrowIfNullOrWhiteSpace(displayName);
// 更新処理...
}
  • string? にすることで「nullは意味がある(未指定)」を型でも表現できるよ🧷✨ (Microsoft Learn)
  • 既存プロジェクトでも、csprojに Nullable を有効化して型で守りやすくできるよ🔒 (Microsoft Learn)
  • 文字列の null/空チェックは ThrowIfNullOrEmpty も便利だよ🛠️ (Microsoft Learn)

6.8 例外の選び方(Preの作法)🎭✅

Pre違反(=呼び出し側が契約違反)には、だいたいこの3つを使うとキレイ✨


6.9 ミニ演習:引数チェックを“ガード節で統一”しよう🏃‍♀️🛡️

お題🎯

次のメソッドの入口チェックを、読みやすいガード節に統一してね✨ (仕様:emailは必須、displayNameは必須、tagsはnull禁止だけど空はOK)

改善前(わちゃわちゃ例)💦

public static void RegisterUser(string email, string displayName, List<string> tags)
{
if (email == null) throw new ArgumentNullException(nameof(email));
if (email.Trim().Length == 0) throw new Exception("email invalid");
if (displayName == null) throw new ArgumentNullException(nameof(displayName));
if (displayName == "") throw new ArgumentException("bad");
if (tags == null) throw new Exception("tags null");

// 本体...
}

ゴール例✅(1つの正解)

public static void RegisterUser(string email, string displayName, List<string> tags)
{
ArgumentException.ThrowIfNullOrWhiteSpace(email);
ArgumentException.ThrowIfNullOrWhiteSpace(displayName);
ArgumentNullException.ThrowIfNull(tags);

// 本体...
}

追加チャレンジ🌟

  • 例外メッセージを揃える(必要なら)💌
  • tagsの中に null / 空白だけ が混ざってたら弾く、も追加してみよう🧼✨

6.10 AI活用(軽め)🤖✨:ガード節の候補を一瞬で出す

AIに頼むときは「仕様(許す/許さない)」を先に渡すのがコツだよ📝💡

  • 「このメソッドは email と displayName は null/空/空白NG、tagsはnullNG・空OK。C#のガード節にして」
  • 「例外型は ArgumentNullException / ArgumentException を使って、入口だけにまとめて」

最後は人が読みやすさチェックしてね👀🎀


6.11 仕上げチェックリスト✅🌈

  • この引数は nullを許す?許さない? を決めた?🕳️
  • 空文字/空配列は 意味がある?ただのミス? を決めた?📭
  • “未指定” を表すなら、**null(型で表現) or 省略(オーバーロード)**に寄せた?🫥
  • 入口チェックは メソッド冒頭にまとまってる? 🚪
  • 例外型は null/空/範囲で自然に選べてる?🎭

次章は、Preを「型」に埋め込んでさらに強くするやつだよ🧷📌(Nullable参照型へ)