それは何ですか?
この例外は、無効なインデックスを使用して、インデックスでコレクションアイテムにアクセスしようとしていることを意味します。インデックスは、コレクションの下限よりも低いか、含まれている要素の数以上の場合は無効です。
投げられたとき
次のように宣言された配列があるとします。
byte[] array = new byte[4];
この配列には0〜3の範囲でアクセスできます。この範囲外の値は IndexOutOfRangeException
。がスローされます。アレイを作成してアクセスするときは、このことを忘れないでください。
配列の長さ
C#では、通常、配列は0ベースです。これは、最初の要素にインデックス0があり、最後の要素にインデックスLength - 1
(Length
配列内の項目の総数)があることを意味するため、次のコードは機能しません。
array[array.Length] = 0;
さらに、多次元配列がある場合は、Array.Length
両方の次元に使用できないため、次のように使用する必要があることに注意してArray.GetLength()
ください。
int[,] data = new int[10, 5];
for (int i=0; i < data.GetLength(0); ++i) {
for (int j=0; j < data.GetLength(1); ++j) {
data[i, j] = 1;
}
}
上限が含まれない
次の例では、の生の2次元配列を作成しますColor
。各アイテムはピクセルを表し、インデックスは(0, 0)
〜(imageWidth - 1, imageHeight - 1)
です。
Color[,] pixels = new Color[imageWidth, imageHeight];
for (int x = 0; x <= imageWidth; ++x) {
for (int y = 0; y <= imageHeight; ++y) {
pixels[x, y] = backgroundColor;
}
}
このコードは、配列が0ベースで、画像の最後(右下)のピクセルが次の理由で失敗しますpixels[imageWidth - 1, imageHeight - 1]
。
pixels[imageWidth, imageHeight] = Color.Black;
別のシナリオではArgumentOutOfRangeException
、このコードを取得できます(たとえばGetPixel
、Bitmap
クラスでメソッドを使用している場合)。
配列が大きくならない
配列は高速です。他のすべてのコレクションと比較して、線形検索は非常に高速です。これは、メモリ内でアイテムが隣接しているため、メモリアドレスを計算できるためです(増分は単なる加算です)。ノードリストをたどる必要はありません、簡単な数学!制限付きでこれを支払います。要素を増やす必要がある場合は、配列を再割り当てする必要があります(古いアイテムを新しいブロックにコピーする必要がある場合、これには比較的長い時間がかかる可能性があります)。でサイズを変更しますArray.Resize<T>()
。この例では、既存の配列に新しいエントリを追加します。
Array.Resize(ref array, array.Length + 1);
有効なインデックスはからである0
ことを忘れないでくださいLength - 1
。単にアイテムを割り当てようとした場合Length
、あなたが得られますIndexOutOfRangeException
(あなたは、彼らがに似た構文で増加すると思われる場合、この動作はあなたを混乱させる可能性Insert
他のコレクションの方法)。
配列のカスタム下限の最初のアイテムを持つ特別な
配列は常にインデックス0を持ちます。カスタムの下限で配列を作成できるため、これは常に当てはまるわけではありません。
var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });
この例では、配列のインデックスは1から4まで有効です。もちろん、上限は変更できません。
誤った引数
(ユーザー入力または関数ユーザーから)検証されていない引数を使用して配列にアクセスすると、次のエラーが発生することがあります。
private static string[] RomanNumbers =
new string[] { "I", "II", "III", "IV", "V" };
public static string Romanize(int number)
{
return RomanNumbers[number];
}
予期しない結果
この例外は別の理由でもスローされる可能性があります。慣例により、多くの検索関数は-1を返します(nullableは.NET 2.0で導入されており、とにかく長年使用されているよく知られた慣例でもあります)。 t何かを見つけます。文字列に相当するオブジェクトの配列があるとしましょう。あなたはこのコードを書くことを考えるかもしれません:
// Items comparable with a string
Console.WriteLine("First item equals to 'Debug' is '{0}'.",
myArray[Array.IndexOf(myArray, "Debug")]);
// Arbitrary objects
Console.WriteLine("First item equals to 'Debug' is '{0}'.",
myArray[Array.FindIndex(myArray, x => x.Type == "Debug")]);
myArray
検索条件を満たすアイテムがない場合、これは失敗します。Array.IndexOf()
-1を返し、配列アクセスがスローます。
次の例は、与えられた数のセットの出現を計算する単純な例です(最大数を知っており、配列0のアイテムが番号0を表し、インデックス1のアイテムが数1を表すというように配列を返します)。
static int[] CountOccurences(int maximum, IEnumerable<int> numbers) {
int[] result = new int[maximum + 1]; // Includes 0
foreach (int number in numbers)
++result[number];
return result;
}
もちろん、それはかなりひどい実装ですが、私が示したいのは、負の数と上記の数では失敗するということです maximum
です。
それがどのように適用されるか List<T>
ますか?
配列と同じ場合-有効なインデックスの範囲-0(List
のインデックスは常に0で始まります)〜list.Count
-この範囲外の要素にアクセスすると例外が発生します。
配列が使用するのと同じケースでList<T>
スローArgumentOutOfRangeException
することに注意してくださいIndexOutOfRangeException
。
配列とは異なり、List<T>
空から始まります-作成したばかりのリストのアイテムにアクセスしようとすると、この例外が発生します。
var list = new List<int>();
一般的なケースは、リストにインデックスを設定することです(と同様Dictionary<int, T>
)で例外が発生します。
list[0] = 42; // exception
list.Add(42); // correct
IDataReaderと列
次のコードを使用して、データベースからデータを読み取ろうとしているとします。
using (var connection = CreateConnection()) {
using (var command = connection.CreateCommand()) {
command.CommandText = "SELECT MyColumn1, MyColumn2 FROM MyTable";
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
ProcessData(reader.GetString(2)); // Throws!
}
}
}
}
GetString()
IndexOutOfRangeException
データセットには2つの列しかないが、3番目の列から値を取得しようとしているため、スローされます(インデックスは常に 0ベースです)。
この動作は、ほとんどのIDataReader
実装(SqlDataReader
、OleDbDataReader
)。
列名を受け取り、無効な列名を渡すインデクサー演算子のIDataReaderオーバーロードを使用した場合にも、同じ例外が発生する可能性があります。
たとえば、Column1という名前の列を取得したが、そのフィールドの値を取得しようとしたとします。
var data = dr["Colum1"]; // Missing the n in Column1.
これは、存在しないColum1フィールドのインデックスを取得しようとしてインデクサーオペレーターが実装されているために発生します。GetOrdinalメソッドは、その内部ヘルパーコードが「Colum1」のインデックスとして-1を返すと、この例外をスローします。
その他
この例外がスローされる別の(文書化された)ケースがあります:でDataView
、DataViewSort
プロパティに提供されているデータ列名が無効な場合。
回避する方法
この例では、簡単にするために、配列は常に1次元で0ベースであると仮定します。あなたは、厳密な(またはライブラリを開発している)になりたい場合は、交換する必要があるかもしれない0
とGetLowerBound(0)
して.Length
とGetUpperBound(0)
(あなたがタイプのパラメータを持っている場合はもちろんSystem.Arra
yは、それが適用されませんT[]
)。この場合、上限はこのコードを含むことに注意してください:
for (int i=0; i < array.Length; ++i) { }
このように書き直す必要があります:
for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) { }
これは許可されていないことに注意してください(これはをスローしますInvalidCastException
)。これが、パラメーターがT[]
カスタム下限配列について安全である場合の理由です。
void foo<T>(T[] array) { }
void test() {
// This will throw InvalidCastException, cannot convert Int32[] to Int32[*]
foo((int)Array.CreateInstance(typeof(int), new int[] { 1 }, new int[] { 1 }));
}
パラメータの検証
インデックスがパラメータからのものである場合は、常にそれらを検証する必要があります(適切なArgumentException
またはをスローしますArgumentOutOfRangeException
)。次の例では、誤ったパラメーターが原因IndexOutOfRangeException
である可能性があります。この関数のユーザーは、配列を渡しているためにこれを予期する可能性がありますが、必ずしもそれほど明白ではありません。常にパブリック関数のパラメーターを検証することをお勧めします。
static void SetRange<T>(T[] array, int from, int length, Func<i, T> function)
{
if (from < 0 || from>= array.Length)
throw new ArgumentOutOfRangeException("from");
if (length < 0)
throw new ArgumentOutOfRangeException("length");
if (from + length > array.Length)
throw new ArgumentException("...");
for (int i=from; i < from + length; ++i)
array[i] = function(i);
}
関数がプライベートの場合、if
ロジックを単に次のように置き換えることができますDebug.Assert()
。
Debug.Assert(from >= 0 && from < array.Length);
オブジェクト状態
配列のインデックスがパラメータから直接取得されない可能性があることを確認します。オブジェクトの状態の一部である可能性があります。一般に、オブジェクトの状態を検証するためには常に(それ自体、および必要に応じて関数パラメーターを使用して)常に良い方法です。を使用しDebug.Assert()
たり、適切な例外をスローしたり(問題についてより詳細に説明したり)、次の例のように処理したりできます。
class Table {
public int SelectedIndex { get; set; }
public Row[] Rows { get; set; }
public Row SelectedRow {
get {
if (Rows == null)
throw new InvalidOperationException("...");
// No or wrong selection, here we just return null for
// this case (it may be the reason we use this property
// instead of direct access)
if (SelectedIndex < 0 || SelectedIndex >= Rows.Length)
return null;
return Rows[SelectedIndex];
}
}
戻り値の検証
前の例の1 つでは、戻り値を直接使用しましたArray.IndexOf()
。失敗する可能性があることがわかっている場合は、そのケースを処理することをお勧めします。
int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
デバッグする方法
私の意見では、このエラーに関するほとんどの質問(ここではSO)は単純に回避できます。適切な質問を書くために費やす時間(小さな作業例と小さな説明を含む)は、コードのデバッグに必要な時間よりもはるかに簡単です。まず、小さなプログラムのデバッグに関するこのEric Lippertのブログ投稿を読んでください。ここで彼の言葉を繰り返すことはしませんが、必ず読む必要があります。
ソースコードがあり、スタックトレース付きの例外メッセージがあります。そこに行って、正しい行番号を選択すると、次のように表示されます。
array[index] = newValue;
エラーが見つかりましたindex
。増加率を確認してください。正しいですか?配列の割り当て方法を確認してくださいindex
。仕様通りですか?はいと答えたらこれらすべての質問にと、StackOverflowで役立つヘルプが見つかりますが、まず自分で確認してください。自分の時間を節約できます!
良い出発点は、常にアサーションを使用し、入力を検証することです。コードコントラクトを使用することもできます。何かがうまくいかず、コードをざっと見ても何が起こるかわからない場合は、昔の友達であるデバッガに頼らなければなりません。Visual Studio(またはお気に入りのIDE)内のデバッグでアプリケーションを実行するだけで、どの行がこの例外をスローし、どの配列が関係し、どのインデックスを使用しようとしているのかが正確にわかります。実際には、99%の時間で自分で解決できます。
これが本番環境で発生する場合は、差別されたコードにアサーションを追加することをお勧めします。おそらく、自分では見えないものはコードに表示されません(ただし、常に賭けることはできます)。
ストーリーのVB.NET側
C#の回答で述べたことはすべて、VB.NETにも有効ですが、構文に明らかな違いがありますが、VB.NET配列を扱う際には、考慮すべき重要な点があります。
VB.NETでは、配列は配列の有効な最大インデックス値を設定して宣言されます。配列に格納する要素の数ではありません。
' declares an array with space for 5 integer
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer
したがって、このループは、IndexOutOfRangeExceptionを発生させることなく、配列を5つの整数で埋めます。
For i As Integer = 0 To 4
myArray(i) = i
Next
VB.NETルール
この例外は、無効なインデックスを使用して、インデックスでコレクションアイテムにアクセスしようとしていることを意味します。インデックスがコレクションの下限よりも低いか、またはより大きい場合、インデックスは無効ですそれが含む要素の数に等しい。 配列宣言で定義された最大許容インデックス