IndexOutOfRangeException / ArgumentOutOfRangeExceptionとは何ですか?どのように修正しますか?

197
Adriano Repetti 2014-01-06 14:19.

私はいくつかのコードを持っています、そしてそれが実行されるとき、それはIndexOutOfRangeException言って、をスローします、

インデックスが配列の範囲外だった。

これはどういう意味ですか、そして私はそれについて何ができますか?

使用するクラスによっては、 ArgumentOutOfRangeException

タイプ 'System.ArgumentOutOfRangeException'の例外がmscorlib.dllで発生しましたが、ユーザーコードで処理されませんでした。追加情報:インデックスが範囲外でした。負ではなく、コレクションのサイズ未満である必要があります。

4 answers

238
Adriano Repetti 2014-01-06 14:19.

それは何ですか?

この例外は、無効なインデックスを使用して、インデックスでコレクションアイテムにアクセスしようとしていることを意味します。コレクションの下限より低いか、含まれる要素の数以上の場合、インデックスは無効です。

投げられたとき

次のように宣言された配列があるとします。

byte[] array = new byte[4];

この配列には0から3までアクセスできますIndexOutOfRangeException。この範囲外の値は、スローされます。配列を作成してアクセスするときは、これを覚えておいてください。

配列の長さ
C#では、通常、配列は0ベースです。これは、最初の要素のインデックスが0で、最後の要素のインデックスLength - 1Length配列内のアイテムの総数)があるため、このコードは機能しないことを意味します。

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、このコードを取得する場合があります(たとえばGetPixelBitmapクラスでメソッドを使用している場合)。

配列が大きくならない
配列は高速です。他のすべてのコレクションと比較して、線形検索で非常に高速です。これは、アイテムがメモリ内で連続しているため、メモリアドレスを計算できるためです(増分は単なる加算です)。ノードリストに従う必要はありません、簡単な計算です!これには制限があります。より多くの要素が必要な場合は、その配列を再割り当てする必要があります(古いアイテムを新しいブロックにコピーする必要がある場合、これには比較的長い時間がかかる可能性があります)。でサイズを変更しますArray.Resize<T>()。この例では、既存の配列に新しいエントリを追加します。

Array.Resize(ref array, array.Length + 1);

有効なインデックスはから0までであることを忘れないでくださいLength - 1。アイテムを割り当てようとすると、Length次のようになりますIndexOutOfRangeExceptionInsert他のコレクションのメソッドと同様の構文でアイテムが増えると思われる場合、この動作は混乱する可能性があります)。

カスタムの下限を持つ特別な配列配列の
最初の項目のインデックスは常に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")]);

-1を返し、配列アクセスがスローさmyArrayれるため、検索条件を満たすアイテムがない場合、これは失敗しArray.IndexOf()ます。

次の例は、特定の数値セットの出現を計算する単純な例です(最大数を認識し、インデックス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を返すと、この例外をスローします。

その他
この例外がスローされる別の(文書化された)ケースがあります:でDataViewDataViewSortプロパティに提供されているデータ列名が無効である場合。

回避する方法

この例では、簡単にするために、配列は常に単次元で0ベースであると仮定します。あなたは、厳密な(またはライブラリを開発している)になりたい場合は、交換する必要があるかもしれない0GetLowerBound(0)して.LengthGetUpperBound(0)(あなたがタイプのパラメータを持っている場合はもちろんSystem.Arrayは、それが適用されません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ルール

この例外は、無効なインデックスを使用して、インデックスでコレクションアイテムにアクセスしようとしていることを意味します。コレクションの下限よりも低いか、よりも大きい場合、インデックスは無効です。含まれている要素の数に等しい。 配列宣言で定義されている最大許容インデックス

20
Lijo 2016-03-05 01:22.

インデックスの範囲外の例外とは何かについての簡単な説明:

1つの列車があり、そのコンパートメントはD1、D2、D3であると考えてください。一人の乗客が電車に乗り込み、D4のチケットを持っています。今何が起こるか。乗客は存在しないコンパートメントに入りたいので、明らかに問題が発生します。

同じシナリオ:配列リストなどにアクセスしようとすると、配列内の既存のインデックスにしかアクセスできません。array[0]array[1]存在しています。アクセスしようとするとarray[3]、実際には存在しないため、インデックスの範囲外の例外が発生します。

10
Snr 2017-12-07 20:48.

問題を簡単に理解するために、次のコードを記述したと想像してください。

static void Main(string[] args)
{
    string[] test = new string[3];
    test[0]= "hello1";
    test[1]= "hello2";
    test[2]= "hello3";

    for (int i = 0; i <= 3; i++)
    {
        Console.WriteLine(test[i].ToString());
    }
}

結果は次のようになります。

hello1
hello2
hello3

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.

配列のサイズは3(0、1、および2を示します)ですが、forループは4回(0、1、2、および3)ループします。
したがって、(3)を使用して境界外にアクセスしようとすると、例外がスローされます。

1
Ricibob 2019-03-01 22:51.

非常に長く完全に受け入れられた回答IndexOutOfRangeExceptionとは別に、他の多くの例外タイプと比較して重要なポイントがあります。それは次のとおりです。

多くの場合、コードの特定のポイントで制御するのが難しい複雑なプログラム状態があります。たとえば、DB接続がダウンして、入力のデータを取得できないなどです。この種の問題により、ある種の例外が発生することがよくあります。それが発生する場所にはその時点でそれを処理する方法がないため、より高いレベルにバブルアップする必要があります。

IndexOutOfRangeExceptionほとんどの場合、例外が発生している時点でチェックするのは非常に簡単であるという点で、一般的に異なります。一般に、この種の例外は、配列の実際の長さをチェックするだけで、発生している場所で問題を非常に簡単に処理できるコードによってスローされます。この例外をより高い位置で処理することによってこれを「修正」したくはありません-代わりに、最初のインスタンスでスローされないようにすることによって-これはほとんどの場合、配列の長さをチェックすることで簡単に行えます。

別の言い方をすれば、入力またはプログラムの状態を完全に制御できないために他の例外が発生する可能性がありますが、IndexOutOfRangeException多くの場合、単なるパイロット(プログラマー)エラーです。

Related questions

MORE COOL STUFF

Reba McEntire は、彼女が息子の Shelby Blackstock と共有する「楽しい」クリスマスの伝統を明らかにしました:「私たちはたくさん笑います」

Reba McEntire は、彼女が息子の Shelby Blackstock と共有する「楽しい」クリスマスの伝統を明らかにしました:「私たちはたくさん笑います」

Reba McEntire が息子の Shelby Blackstock と共有しているクリスマスの伝統について学びましょう。

メーガン・マークルは、自然な髪のスタイリングをめぐってマライア・キャリーと結ばれました

メーガン・マークルは、自然な髪のスタイリングをめぐってマライア・キャリーと結ばれました

メーガン・マークルとマライア・キャリーが自然な髪の上でどのように結合したかについて、メーガンの「アーキタイプ」ポッドキャストのエピソードで学びましょう.

ハリー王子は家族との関係を修復できるという「希望を持っている」:「彼は父親と兄弟を愛している」

ハリー王子は家族との関係を修復できるという「希望を持っている」:「彼は父親と兄弟を愛している」

ハリー王子が家族、特にチャールズ王とウィリアム王子との関係について望んでいると主張したある情報源を発見してください。

ワイノナ・ジャッドは、パニックに陥った休暇の瞬間に、彼女がジャッド家の家長であることを認識しました

ワイノナ・ジャッドは、パニックに陥った休暇の瞬間に、彼女がジャッド家の家長であることを認識しました

ワイノナ・ジャッドが、母親のナオミ・ジャッドが亡くなってから初めての感謝祭のお祝いを主催しているときに、彼女が今では家長であることをどのように認識したかを学びましょう.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

オーケーグッド770HPランボルギーニセンテナリオは十分に正気ではない

オーケーグッド770HPランボルギーニセンテナリオは十分に正気ではない

ランボルギーニの創設者であるフェルッチオランボルギーニが100歳になるのは毎日ではありません(そうです、彼は死んでいて、まだ死んでいると思います。

彼らが買った1台の車からAppleの車の計画について私たちが推測できること

彼らが買った1台の車からAppleの車の計画について私たちが推測できること

Appleが自動車分野に参入するという噂はかなり前から渦巻いており、AppleウォッチャーがSixtyEight Researchという会社がAppleの自動車研究開発のシェル会社である可能性が高いと判断したとき、その渦巻きは本当に渦巻いた。また、会社が購入した車は1台だけであることが知られており、その車はAppleが何を考えているかについての手がかりでいっぱいになる可能性があることも伝えています。

天文学者は太陽系の9番目の惑星の新しい証拠を見つけます

天文学者は太陽系の9番目の惑星の新しい証拠を見つけます

太陽系の外側にある架空の大きな物体である惑星Xの探索は、何十年にもわたって人間を魅了してきました。その検索の最新の章は、地球の10倍の大きさで、公転周期が15であるほど遠くにある惑星を指しています。

キャムニュートン、ゴッドダム

キャムニュートン、ゴッドダム

カムニュートンは昨日、簡単な265ヤードと3回のタッチダウンでファルコンズを引き裂き、別の素晴らしいゲームをしました。その日のハイライトは、上のタッチダウンスローでした。これは、視聴するたびにばかげているだけです。

米国のフィギュア スケートは、チーム イベントでの最終決定の欠如に「苛立ち」、公正な裁定を求める

米国のフィギュア スケートは、チーム イベントでの最終決定の欠如に「苛立ち」、公正な裁定を求める

ロシアのフィギュアスケーター、カミラ・バリエバが関与したドーピング事件が整理されているため、チームは2022年北京冬季オリンピックで獲得したメダルを待っています。

Amazonの買い物客は、わずか10ドルのシルクの枕カバーのおかげで、「甘やかされた赤ちゃんのように」眠れると言っています

Amazonの買い物客は、わずか10ドルのシルクの枕カバーのおかげで、「甘やかされた赤ちゃんのように」眠れると言っています

何千人ものAmazonの買い物客がMulberry Silk Pillowcaseを推奨しており、現在販売中. シルクの枕カバーにはいくつかの色があり、髪を柔らかく肌を透明に保ちます。Amazonで最大46%オフになっている間にシルクの枕カバーを購入してください

パデュー大学の教授が覚醒剤を扱った疑いで逮捕され、女性に性的好意を抱かせる

パデュー大学の教授が覚醒剤を扱った疑いで逮捕され、女性に性的好意を抱かせる

ラファイエット警察署は、「不審な男性が女性に近づいた」という複数の苦情を受けて、12 月にパデュー大学の教授の捜査を開始しました。

コンセプト ドリフト: AI にとって世界の変化は速すぎる

コンセプト ドリフト: AI にとって世界の変化は速すぎる

私たちの周りの世界と同じように、言語は常に変化しています。以前の時代では、言語の変化は数年または数十年にわたって発生していましたが、現在では数日または数時間で変化する可能性があります。

SF攻撃で91歳のアジア人女性が殴られ、コンクリートに叩きつけられた

犯罪擁護派のオークランドが暴力犯罪者のロミオ・ロレンゾ・パーハムを釈放

SF攻撃で91歳のアジア人女性が殴られ、コンクリートに叩きつけられた

認知症を患っている 91 歳のアジア人女性が最近、47 番街のアウター サンセット地区でロメオ ロレンゾ パーハムに襲われました。伝えられるところによると、被害者はサンフランシスコの通りを歩いていたところ、容疑者に近づき、攻撃を受け、暴行を受けました。

ℝ

“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.

メリック・ガーランドはアメリカに失敗しましたか?

バイデン大統領の任期の半分以上です。メリック・ガーランドは何を待っていますか?

メリック・ガーランドはアメリカに失敗しましたか?

人々にチャンスを与えることは、人生で少し遅すぎると私は信じています。寛大に。

Language