IndexOutOfRangeException(「インデックスが配列の境界外です」)は、配列やリストの範囲外を指したときに起きる。原因はほぼ「数え方・境界条件の思い込み」で、再現条件がはっきりしている分、直し方も型が決まっている。
先に結論
- 有効な添字は 0 〜 Length – 1。ここを外したら必ずこの例外になる
- 定番はループ条件の
<= と、空配列への [0] アクセス - 外部入力を Split した結果など、要素数が想定と違うケースも多い
- 防ぎ方は「境界を先に確認する」か「添字を使わない書き方(foreach)に寄せる」
よくある発生パターン
- ループ条件を
i <= array.Length と書いて1つ行き過ぎる - 要素数 0 の配列・リストで先頭
[0] や末尾 [Count - 1] を取る - CSV などを Split した結果の列数が、行によって違う(末尾の区切り・空行)
- 多次元配列とジャグ配列(配列の配列)で、次元ごとの長さを取り違える
- 並列処理で共有変数の添字を進めて、想定範囲を超える
切り分け方
- 例外が出た行で、添字の値と対象の Length / Count をログやデバッガで確定する
- 「どのデータで落ちるか」を特定する。特定行・特定入力で落ちるなら、データ側の要素数不足が濃厚
- 常時ではなく稀に落ちる場合は、並行処理と境界データ(空・1件)を疑う
- なお
List<T> の添字範囲外は、似ているが別例外の ArgumentOutOfRangeException になる。例外名が違う場合はそちらで調べる
対策と書き換え
添字アクセスが本当に必要かを見直すのが先。走査だけなら foreach に寄せると境界バグが消える。
foreach (var item in items)
{
// 添字を使わないので境界を踏まない
}
// 添字が必要な場合のガード
if (index >= 0 && index < items.Count)
{
var target = items[index];
}
- 末尾アクセスは
items[^1] のような末尾指定も使えるが、空チェックは別途必要 - 外部データを分ける処理では、要素数の検証を「引く前」に置く
- マジックナンバーで列位置を決めている箇所は、想定外データで真っ先に割れる
予防
- 境界ケース(0件・1件・想定最大)のテストを必ず置く。この例外は境界でしか出ない
- 配列の長さを前提にした処理は、前提をコメントではなく検証コードで残す
- ループは
< を既定にし、<= を書いたら理由を確認する習慣を付ける
ざっくりまとめ
- 原因は境界の思い込み。
<= と空データの [0] が二大発生源 - 切り分けは「添字の値と Length」をその場で確定すること
- foreach 化と境界テストで、構造的に踏まなくできる
関連記事