ArgumentOutOfRangeException の原因と直し方
List<T>のインデクサに範囲外の値を渡すと発生します。配列のIndexOutOfRangeExceptionとは異なる例外クラスです。
エラーメッセージの読み方
Unhandled exception. System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
System- 名前空間 — System なら標準の基本例外です
ArgumentOutOfRangeException- 例外クラス — 何が起きたか。ここを検索するのが最短です
Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')- 詳細メッセージ — どの値が問題だったか
at Program.Main(String[] args) in Program.cs:line 4- 発生箇所 — スタックトレースの先頭行
このエラーが出る典型パターン
パターン1
1 class Program { 2 static void Main(string[] args) { 3 var nums = new System.Collections.Generic.List<int> { 1, 2, 3 }; 4 System.Console.WriteLine(nums[5 ]); ^ 5 } 6 }
List<T>のインデクサは配列と違い、範囲外を指定するとArgumentOutOfRangeExceptionを投げます(配列のIndexOutOfRangeExceptionとは型が違います)。
直し方: 5 を 1 にします。
パターン2
1 class Program { 2 static void Main(string[] args) { 3 var nums = new System.Collections.Generic.List<int> { 1, 2, 3 }; 4 int idx = -1; ^ 5 System.Console.WriteLine(nums[idx]); 6 } 7 }
負のインデックスも同様にArgumentOutOfRangeExceptionになります。
直し方: -1 を 0 にします。
パターン3
1 class Program { 2 static void Main(string[] args) { 3 var names = new System.Collections.Generic.List<string> { "a", "b" }; 4 System.Console.WriteLine(names[2 ]); ^ 5 } 6 }
要素数2のリストで有効なインデックスは0と1だけです。Countと最大インデックスを混同しないようにします。
直し方: 2 を 1 にします。
パターン4
1 class Program { 2 static void Main(string[] args) { 3 var vals = new System.Collections.Generic.List<int> { 4, 8, 15 }; 4 System.Console.WriteLine(vals[9 ]); ^ 5 } 6 }
List<T>のインデクサも配列と同様、範囲外を指定すると例外になります。
直し方: 9 を 2 にします。
パターン5
1 class Program { 2 static void Main(string[] args) { 3 var items = new System.Collections.Generic.List<string> { "x", "y", "z" }; 4 int idx = 3 ; ^ 5 System.Console.WriteLine(items[idx]); 6 } 7 }
要素数3のリストで有効なインデックスは0〜2までです。3は1つ超えています。
直し方: 3 を 0 にします。
よくある誤解
配列とList<T>は似ていますが、範囲外アクセス時に投げる例外の型が違います。catchするときはこの違いに注意が必要です。
実務での勘所
配列とList<T>で例外の型が違うのは、実装の層が違うためです。配列は.NET 1.0の時代からCLRに直接組み込まれたプリミティブな存在で、専用のIndexOutOfRangeExceptionを持っています。List<T>は.NET 2.0でジェネリクスとともに追加された、C#自身で書かれた通常のクラスで、範囲チェックの実装には「引数の値が許容範囲外である」という、BCL全体で汎用的に使われるArgumentOutOfRangeExceptionを利用しています。catchするコードを書くときは、対象がT[]なのかList<T>なのかで例外の型を正しく使い分ける必要があります。