ArgumentOutOfRangeException について

初級者向けにおさらいします。

目次

この例外エラーの説明

Argument(メソッドに、引数で渡されてきた変数の値が)OutOfRange(想定している範囲内ではなかった、範囲から超えている場合に発生する)Exception です。渡されてきた引数の値が、メソッド先の処理内で、何かの範囲値に関係する際に発生します。

事例とその対処方法

例外エラーは、想定外の扱われ方をすると発生して飛んで来ます。それは命令の使い方が間違っていたり、存在しないデータを扱おうとしていたり、アカウント権限を越えたアクセスをしようとしていたり(Windows 側やウェブ側の話だったり)、サービスが動いていないのに連携しようとしたり、製品上の仕様考慮モレだったり、そういう系です。

こうなっている前提のはずだから、こうやろうとしたのに、実際はここがこうなっているからダメじゃん!こういう場合の処理が無いじゃん!みたいなコードになっていませんか?

String

1つ目

Dim name = "YamadaTaro"
Dim firstName = name.Substring(6, 10)
System.ArgumentOutOfRangeException: 'インデックスおよび長さは文字列内の場所を参照しなければなりません。
パラメーター名:length'

SubString メソッドは一部の文字列を抽出してくるメソッドです。第一引数には抽出したい文字列の開始位置(0 始まり)、第二引数は、抽出したい文字列の終了位置 ではなく 第一引数の位置から何文字分抽出するか?を指定します。start, end ではなく start, length なんですね。正しくは name.Substring(6, 4) と指定します。

2つ目

Dim data = "pen, pineapple, apple, pen"
Dim index = data.IndexOf("banana")
Dim found = data.Substring(index, "banana".Length)
System.ArgumentOutOfRangeException: 'StartIndex の値を 0 より小さくすることはできません。
パラメーター名:startIndex'

この事例では、検索処理が見つかった場合しか考慮されていないソースコードになります。データの中からは "banana" は見つからなかったので、-1 が返却されています。開始位置が -1 はおかしい値になりますので、例外エラーが発生してしまいます。

ぜひ、もしも、別の場合だったらどうするか? と考える習慣を身に着けてください。

List(Of T)

1つ目

Dim items = New List(Of Integer) From {1, 2, 3}
Dim _3rdItem = items(3)
System.ArgumentOutOfRangeException: 'インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメーター名:index'

コレクションの3つ目が欲しい時に、3を指定するのは間違っていて、0始まりなので3-1で 2 を指定する必要があります。正しくは items(2) と指定します。

2つ目

Dim items = New List(Of Integer) From {1, 2, 3}
For i As Integer = 0 To items.Count - 1
    items.RemoveAt(i)
Next
System.ArgumentOutOfRangeException: 'インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメーター名:index'

ループ中にコレクションを削除したり追加したりするのはダメです。

i=0, items {1, 2, 3}
↓
i=0, items {2, 3} 【OK】items コレクションのインデックス値0(1個目)を削除しようとした

i=1, items {2, 3}
↓
i=1, items {2} 【OK】items コレクションのインデックス値1(2個目)を削除しようとした

i=2, items {2}
↓
i=2, items {2} 【NG】items コレクションのインデックス値2(3個目)を削除しようとした、3個目は存在しない

コレクションを編集したい場合は、ループ用と編集用の2つ用意して編集します。

Dim items = New List(Of Integer) From {1, 2, 3}
Dim works = items.ToList()
For i As Integer = 0 To items.Count - 1
    works.RemoveAt(i)
Next

ただし、これだけではダメで、削除できるかの事前チェックを行うように修正します。

For i As Integer = 0 To items.Count - 1

    If i < works.Count Then
        works.RemoveAt(i)
    End If

Next