0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

R14:【掟・判例】 _ (discard) の使いどころ: out/戻り値を捨てる基準

Last updated at Posted at 2026-01-16

連載Index(読む順・公開済(リンク)はここが最新): S00_門前の誓い_総合Index

最初は意図があり、未使用警告を消せる喜びを覚える。
戻り値は要らない。outも要らない。だから _ を置きたくなる。

便利で、手軽で中毒になりそうになる。

ただ、中毒になり油断して _ が増えてくると話が変わる。
同じ記号が「捨てる(discard)」「変数名」「それ以外」に化け、意図が読めなくなる。
「捨てて良い値」と「捨てたら切り分けが遅れる値」まで同じ _ に並び、後から見直すと判断がつかない。

このページは、_ を置く場面と置かない場面を、戻り値/out/タプル/パターンで線引きする。

このページのゴール

  • _ が何を意味するか(捨てる/変数名/パターン)を先に切り分ける
  • 「捨てて良い値/捨てると困る値」を判例で明確化する
  • 戻り値とoutのどちらを主に読むか、判断基準を揃える
  • 使いどころ(out/タプル/パターン/_ = 呼び出し)を同じルールで扱う
  • 現場の指摘が感情論にならないコメント例まで落とす

先に揃える: _ は「捨てる」「変数名」「パターン」で別物

1) discard(捨てる)としての _

「この値は使わない」をコード上で明文化するための _。
未使用警告を消す目的だけでなく、読み手に意図を残す用途になる。

  • outの受け取りで捨てる(= out discard)
  • 分解代入(タプル/Deconstruct)で一部を捨てる
  • 戻り値を捨てる(_ = 呼び出し)

2) 識別子(変数名)としての _

_ はC#の識別子として有効な名前でもある。
そのため「捨てるつもりの _」が、ただの変数になって意味がねじれる事故が起きる。

  • _ がdiscardとして扱われるのは、スコープ内に _ という識別子が存在しない場合
  • _ を通常の変数名として宣言すると、以降の _ はdiscardではなく「その変数」になる
// これはdiscard(捨てる)として読める
_ = GetValue();

// ここで _ を変数として宣言すると、以降はdiscardではなく「変数_」になる
var _ = 123;
_ = GetValue(); // 「捨てる」ではなく「変数_に代入」になる

このページでは、運用として _ を変数名に使わない前提で話を進める(混ざると読みが壊れるため)。

3) パターンの _ (discard pattern)

パターンの _ は「それ以外」を表す分岐の記号で、discard変数でも変数名でもない。
スコープに _ があっても、パターンの _ は「既存の変数 _」にはならない。

string kind = token switch
{
    Token.Number => "num",
    _ => "other",
};

用語を最短で明文化する(読むために必要な分だけ)

  • タプル: 複数の値をまとめて返す戻り値の形。例: (bool ok, string message)
    var (ok, message) = ...; のように受け取り側で分解して使う。
  • パターン: switchやisで「形で分岐」する書き方。例: x switch { 0 => ..., _ => ... }
    ここの _ は「それ以外」という分岐の記号で、変数ではない。

再発防止の掟: _ で捨てる基準

  • 「この値は使わない」を明文化したい場面のみ _ を使う
    なぜ: 読み手に意図が伝わり、未使用警告の抑止にも寄るため
  • 捨てると判断が遅れる値(原因切り分けに必要な値)は捨てない
    なぜ: 事故時に「取っておけば分かった」が一番高く付くため
  • 戻り値とoutの両方があるAPIは、どちらを主に読むかを先に決める
    なぜ: 呼び出し側が場当たりになると、捨て方が揺れて議論が止まるため
  • _ を変数名として運用しない
    なぜ: discardとして読む前提が崩れ、コードの意味がねじれるため
  • 使わないoutが常態化するなら、呼び出し側で誤魔化さず設計を見直す
    なぜ: 「本当は必要な情報」を捨てる運用に寄り、障害時に情報不足が起きるため

最短チェックリスト(読みが止まった時に見る所)

  • _ が「変数として宣言」されていないか(スコープ汚染の有無)
  • _ の位置はどれか(out/分解代入/_ = 呼び出し/パターン)
  • 捨てた値は「成否」「分岐」「原因切り分け」に必要ではないか
  • 捨てた値を保持すると、ログや例外で証拠が残せるか
  • 同じAPIの呼び出しで、戻り値とoutの主役が揺れていないか

採用判断: どの値を捨てるか

1) outだけ捨てたい(out discard)

戻り値だけが必要で、outは使わない場面に合う。

// 例: 戻り値(int)だけ必要で、outのモデルは不要
int code = GetStatusCode(out Model _);

2) 戻り値だけ捨てたい(_ = 呼び出し)

呼び出し自体に意味があり、戻り値は不要な場面に合う(副作用やウォームアップなど)。
戻り値を捨てる意思表示を残す目的で使う。

// 例: キャッシュを温めるだけで戻り値は不要
_ = WarmUpCache();

WarmUpCache();だけの行は「何のために呼んでいるか」が残りにくい。副作用の見落としが起きやすくなる。

3) タプル/分解代入で一部だけ捨てたい

「要素の意味が曖昧」なまま捨てると、失敗時の切り分けが遅れる。
特に成否やステータスを捨てると事故になりやすい。

// 役割がある要素は保持する(成否は捨てない)
var (ok, message) = TryGetMessage();

// これは危険になりやすい(成否を捨てると失敗時に読めない)
var (_, message2) = TryGetMessage();

4) パターンで「それ以外」を表したい

パターンの _ は「変数名」でも「discard変数」でもなく、分岐の意図が明確になる。

bool enabled = state switch
{
    State.Ready => true,
    _ => false,
};

5) outと戻り値の「どちらが主役か」を揃える

同じAPIを複数箇所で使う場合、主役が揺れると読みが止まりやすい。
典型はTry系(outが主役)と、戻り値が主役の関数が混ざるケース。

// Try系: 成功判定が主役、outは成功時の値
bool ok = int.TryParse(text, out int value);

// 戻り値が主役: 戻り値が意味を持つなら out は補助
int result = GetResult(out Model model);

「戻り値を読むのかoutを読むのか」を決めると、_ の使いどころも揃えられる。

判例(OK/NG)

観点 OK例 NG例 理由(事故) 見る所(確認ポイント)
outを使わない意思表示 int code = GetStatusCode(out Model _); int code = GetStatusCode(out Model m); // m未使用 未使用変数が増え、意図も曖昧になる out値が本当に不要か
戻り値を捨てる意思表示 _ = WarmUpCache(); WarmUpCache();の乱発で意図が不明 呼ぶ理由が読めないと副作用の見落としが起きる 呼び出し自体に意味があるか
捨てると困る値を保持 var (ok, message) = TryGetMessage(); var (_, message) = TryGetMessage(); 成功判定を捨てると失敗時に切り分けが遅れる 捨てた値が判断に必要か
_ を変数名にしない var unused = 0; var _ = 0; discardと混ざり、読みが壊れる _ を通常の識別子にしていないか
outを捨てるより設計見直し bool ok = TryGet(out value); int code = Get(out _);が多発 使わないoutが常態化するとAPI設計が歪む outに意味がある設計か

容赦なき断罪: 現場の指摘観点

止まるのは _ そのものではなく「捨てた理由が読めない」時。
未使用警告を消しただけの _ は、後から読む人にとって情報欠落になる。
特に障害対応では「捨てた値」が切り分けの鍵になり、調査時間が伸びる。

観点 ありがちな見落とし 困ること(現場の損失) 指摘コメント例(直球禁止)
捨てて良い値か 何となく _ で捨てる 障害時に情報不足で切り分けが遅れる 捨てた値が切り分け材料になりそうなので、保持した方が調査コストが下がりそう
outと戻り値の主役 呼び出しごとに主役が揺れる 読む側が毎回仕様を推測し、議論が止まる このAPIは戻り値(out)が主役に見えるため、使い方を揃えると読みが止まりにくい
_ を変数名にしている discardのつもりで導入 以降の _ が「捨てる」なのか「変数」なのか分からなくなる _ を識別子として使うとdiscardの意味が崩れるため、別名に寄せたい
_ = 呼び出し の意図 呼ぶだけの行が増える 副作用の見落としや二重呼びが起きる 戻り値を捨てて呼ぶ理由が重要に見えるため、目的が伝わる形に寄せたい
タプルで判定を捨てる 成否(ok)を _ で捨てる 失敗しても気づけず、後段で別の例外として爆発する 成否が後段の前提に見えるため、判定を保持すると読みが安定しそう

禁書庫A: 逆引き(症状→原因→対策)

症状 ありがちな原因 切り分け(見る場所) 最短の対処 再発防止(ルール化)
_ を見て読むのが止まる discardと変数名の区別がない _ が宣言されていないか _ を変数名に使う箇所を排除 _ はdiscard専用にする
outの未使用警告が多い outを受けて捨てている out引数の用途 out discardへ寄せて意図を明示 outが不要ならAPI役割を見直す
戻り値を捨てて良いか迷う 呼び出し理由が不明 呼び出しが副作用目的か _ =で捨てる意図を明示 「副作用目的のみ _ =」を運用化
タプルの _ が読めない 要素の意味が曖昧 タプル要素名/順序 役割が必要な要素は捨てない 捨てるのは判断不要な要素のみ
事故調査で情報不足 捨てた値が判断材料だった ログ/分岐条件 捨てずに保持してログへ回す 「切り分け値は捨てない」を規約化

禁書庫B: チートシート(決め打ちで読む)

状況 判断
outが不要 Get(out Type _) 使わない意思表示として通す
呼ぶこと自体が目的 _ = WarmUp(); 意図が伝わるなら通す
成否/分岐に必要 (_, value)の形 捨てない(判定材料は保持)
_ を変数名にしている var _ = ... 運用上NG(意味が崩れる)
使わないoutが常態化 Get(out _)が多発 API役割の見直し候補
.NET Framework 4.8での扱い(差分が出る所だけ)
  • _ をdiscardとして使う構文はC# 7.0以降の機能になる
    .NET Framework 4.8でも、プロジェクトのC#言語バージョンが古いと構文が使えない
  • 差はランタイムではなく、コンパイラ(言語バージョン)側で出る
    同じ.NET Framework 4.8でも、開発環境やLangVersionで可否が変わる

関連トピック

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?