• システム開発に関わる内容をざっくりと書いていく

C# IndexOutOfRangeException 発生原因と対策

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 化と境界テストで、構造的に踏まなくできる

関連記事