はじめに:「安全取得」は“落ちないコード”を書くための必須スキル
業務システムで一番イヤなのは、「たまたまデータがなかっただけで落ちる」パターンです。IndexOutOfRangeException、InvalidOperationException、NullReferenceException…どれも「ちゃんと安全取得していれば防げた」ものばかりです。
ここでいう「安全取得」は、
- コレクションから「なかったらどうするか」を決めて値を取る
- Dictionary から「なかったらどうするか」を決めて値を取る
- LINQ で「0件のときに例外にしない」取り方をする
といった、「例外を出さずに、意図した形で値を扱うテクニック」のことです。
インデックスアクセスの安全取得
配列・List の「添字アクセス」は危険な刃
まず、典型的な危険パターンから。
var list = new List<string> { "A", "B", "C" };
var x = list[5]; // IndexOutOfRangeException
C#業務では、「たまたま件数が少なかった」「フィルタした結果が短くなった」だけで、こういう例外が起きます。
安全に書くなら、「範囲内かどうか」を先に確認します。
int index = 5;
string? value = null;
if (0 <= index && index < list.Count)
{
value = list[index];
}
C#ここでの重要ポイントは、「“存在しないインデックス”を普通に使うと落ちる」という当たり前を、コードの中でちゃんと表現することです。
LINQ の安全取得:FirstOrDefault / SingleOrDefault / ElementAtOrDefault
First と FirstOrDefault の違い
LINQ で一番よく使うのが First と FirstOrDefault です。
using System.Linq;
var numbers = new List<int> { 1, 2, 3 };
// 1件以上ある前提なら First
int first = numbers.First(); // 1
C#でも、空のときは例外になります。
var empty = new List<int>();
int x = empty.First(); // InvalidOperationException
C#「0件かもしれない」なら、FirstOrDefault を使います。
int firstOrDefault = empty.FirstOrDefault(); // 0(int の既定値)
C#参照型なら null が返ります。
var names = new List<string>();
string? name = names.FirstOrDefault(); // null
C#ここでの重要ポイントは、「First は“必ずある前提”、FirstOrDefault は“ないかもしれない前提”」という使い分けです。
業務的に「必ず1件以上あるべき」なら First で例外にしてもよく、「0件も普通にあり得る」なら FirstOrDefault を選びます。
条件付き FirstOrDefault
条件付きでも同じです。
var employees = new List<Employee>
{
new Employee { No = 1, Name = "Sato" },
new Employee { No = 2, Name = "Suzuki" },
};
var emp = employees.FirstOrDefault(e => e.No == 99); // 見つからない → null
C#「見つからないこともある」前提なら、null を許容する設計にしておきます。
if (emp is null)
{
// 見つからなかったときの処理
}
C#Single / SingleOrDefault の安全な使い方
Single は「ちょうど1件だけあるはず」というときに使います。
var e = employees.Single(x => x.No == 1);
C#0件でも2件以上でも例外になります。
「1件だけであるべき」という業務ルールをコードに刻むためのメソッドです。
「0件もあり得るが、2件以上はおかしい」というときは SingleOrDefault を使います。
var e = employees.SingleOrDefault(x => x.No == 1); // 0件なら null、2件以上なら例外
C#ここでの重要ポイントは、「Single 系は“件数の正しさ”をチェックするための道具」だということです。
安全取得というより、「データ不整合を早期にあぶり出す」ために使います。
ElementAtOrDefault でインデックスの安全取得
LINQ には、インデックス版の安全取得もあります。
var list = new List<string> { "A", "B", "C" };
string? v1 = list.ElementAtOrDefault(1); // "B"
string? v9 = list.ElementAtOrDefault(9); // null
C#list[9] なら例外ですが、ElementAtOrDefault(9) なら既定値(参照型なら null)になります。
Dictionary の安全取得:TryGetValue と「デフォルト値」
TryGetValue で「ないかもしれない」を前提に書く
Dictionary の安全取得の主役は TryGetValue です。
var map = new Dictionary<string, string>
{
["A"] = "りんご",
["B"] = "バナナ",
};
if (map.TryGetValue("A", out var name))
{
Console.WriteLine(name);
}
else
{
Console.WriteLine("見つかりません");
}
C#ここでの重要ポイントは、「例外ではなく“分岐”で制御している」ことです。dict[key] は「ある前提」、TryGetValue は「ないかもしれない前提」です。
「なかったらデフォルト値」を返すユーティリティ
毎回 if を書くのが面倒なら、拡張メソッドで「安全取得+デフォルト値」をまとめてもよいです。
public static class DictionaryExtensions
{
public static TValue? GetOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> source,
TKey key,
TValue? defaultValue = default)
where TKey : notnull
{
return source.TryGetValue(key, out var value)
? value
: defaultValue;
}
}
C#使い方はこうです。
var name = map.GetOrDefault("X", "不明"); // 見つからなければ "不明"
C#ここでの重要ポイントは、「“なかったときどうするか”をメソッドの引数で決めている」ことです。
呼び出し側の if が減り、意図が読みやすくなります。
null 安全取得:?. と ?? の組み合わせ
コレクション自体が null のとき
業務コードでは、List<T>? や IEnumerable<T>? が null のこともよくあります。
List<string>? names = GetNamesOrNull();
C#このとき、いきなり names.FirstOrDefault() と書くと NullReferenceException です。
安全に書くなら、こうです。
string? first = names?.FirstOrDefault();
C#names が null なら、first も null になります。
「null なら空扱い」にする
「null =データなし」とみなして、空列として扱いたいことも多いです。
var safeNames = names ?? Enumerable.Empty<string>();
string? first = safeNames.FirstOrDefault();
C#ここでの重要ポイントは、「null を“例外の原因”にするか、“0件扱い”にするかを設計で決める」ことです。
多くの集計・一覧処理では、「null なら空」としてしまうほうが扱いやすくなります。
実務で意識してほしい「安全取得」の設計視点
1件もないのは「普通」か「異常」か
安全取得を考えるとき、必ず自分に問いかけてほしいのがこれです。
- 0件なのは、業務的に普通にあり得るか?
- それとも、0件はおかしい(データ不整合)か?
普通にあり得るなら、FirstOrDefault や TryGetValue で「なかったときの振る舞い」を書く。
おかしいなら、あえて First や Single で例外にして、バグとして気づけるようにする。
ここを曖昧にすると、「たまたま0件だっただけで落ちる」か、「おかしなデータでも気づかない」かのどちらかになります。
「なかったときどうするか」をコードに刻む
安全取得は、「なかったときの方針」をコードに埋め込む作業です。
- null を返す(呼び出し側で判断させる)
- デフォルト値を返す(0、””、”不明” など)
- ログを出してスキップする
- 例外にして止める
どれを選ぶかは業務次第ですが、「何も考えずに First() や dict[key] を書く」のは、ほぼ確実に後で痛みます。
まとめ:「安全取得ユーティリティ」は“現場で落ちないコード”の土台
安全取得の本質は、
「データが“ないかもしれない”現実を、ちゃんとコードに反映する」
ことです。
押さえておきたい道具は、
- インデックス:範囲チェック or
ElementAtOrDefault - LINQ:
FirstOrDefault/SingleOrDefault(0件を許容するかどうか) - Dictionary:
TryGetValueとGetOrDefault的なユーティリティ - null:
?.と??、?? Enumerable.Empty<T>()
そして何より大事なのは、
「0件/キーなし/null は、業務的にどう扱うべきか?」
を先に決めてからコードを書くことです。
ここが腹に落ちると、「とりあえず取ってみて、落ちたら直す」スタイルから卒業して、
“最初から落ちない設計で書く”エンジニア側の思考に、一段レベルアップできます。
