VB のたまご

作成日: 2015/12/03, 更新日: 2015/12/03


ArgumentOutOfRangeException について

概要

  •  ArgumentOutOfRangeException は、メソッドに渡した引数が、適切な範囲を超えた値の場合に、発生する例外エラーです。 配列やリストを扱う際に多く発生し、インデックス値が存在しない値を指定しているのが原因です。


スポンサーリンク


事例と対処方法

  •  以下のサンプルは、意図的に例外エラーを発生させるコードです。 実際の業務プログラムでは、このような簡単なコードではないと思いますが、例外発生させている、直接的な原因となる部分として参考になるはずです。 また、本例外エラーが発生してしまう原因となった、根本的な原因となる別の例外エラーが、さらに奥深くにいる場合があります。 (本例外エラーは間接的に影響を受けて発生してしまったもので、本当の原因となる例外エラーを食い止めることで、本例外エラーも直る場合があります。)

  •  このような組み合わせのパターンは、本例外エラーに限らず、他の例外エラー全般にも言えることですが、そんなに頻繁に出くわすものではないと思いますので、 そういう場合もあるということだけ、覚えておいていただけると解決しやすくなるのではと思います。

  •  また、記載の対処方法は、あくまでも一例です。 他の対処方法の方が、その業務プログラムにとってベストプラクティスとなる場合もありますので、検討材料の1つという程度に確認していただければと思います。

  • スポンサーリンク



  • String.Substring、その1
  • Dim s1 As String = "hello"
    Dim s2 As String = s1.Substring(-1)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: StartIndex の値を 0 より小さくすることはできません。
    パラメーター名:startIndex
       場所 System.String.Substring(Int32 startIndex, Int32 length)
       場所 System.String.Substring(Int32 startIndex)
    
    ' 対処方法
    0 始まりに気を付け、存在する範囲のインデックスを指定する
    
  •  Substring メソッドに指定するインデックス値は、0 始まりであることに注意します。 Substring メソッドは、第一引数:開始位置の設定、0~をセットする。第二引数:開始位置から「何文字分」取得するか指定する。という仕様です。 特に、第二引数は「終了位置」ではなく「取得する文字数」という点に注意が必要です。


  • String.Substring、その2
  • Dim s1 As String = "[hello]"
    ' s1 に入っている「はず」という思い込み
    Dim startIndex As Integer = s1.IndexOf("abc")
    Dim s2 As String = s1.Substring(startIndex)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: StartIndex の値を 0 より小さくすることはできません。
    パラメーター名:startIndex
       場所 System.String.Substring(Int32 startIndex, Int32 length)
       場所 System.String.Substring(Int32 startIndex)
    
    ' 対処方法
    インデックスの取得に注意
    
  •  String.IndexOf メソッドで指定するキーワードが、毎回必ず含まれているという思い込みからの例外エラーです。 固定でも大丈夫な仕様であれば問題ありませんが、キーワードが可変する仕様であれば、見直しが必要です。


  • String.Substring、その3
  • Dim s1 As String = "hello"
    Dim s2 As String = s1.Substring(6)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: startIndex に文字列の長さより大きい値を指定することはできません。
    パラメーター名:startIndex
       場所 System.String.Substring(Int32 startIndex, Int32 length)
       場所 System.String.Substring(Int32 startIndex)
    
    ' 対処方法
    0 始まりに気を付け、存在する範囲のインデックスを指定する
    
  •  最初の事例と同様です。0 始まりで数えるので、インデックスが1つ分大きい値になってしまっています。


  • String.Substring、その4
  • Dim s1 As String = "[hello]"
    Dim s2 As String = s1.Substring(1, s1.Length)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: インデックスおよび長さは文字列内の場所を参照しなければなりません。
    パラメーター名:length
       場所 System.String.Substring(Int32 startIndex, Int32 length)
    
    ' 対処方法
    Dim s1 As String = "[hello]"
    Dim s2 As String = s1.Substring(1)
    
  •  開始と終了にあるかっこの文字を、除去する処理でのエラーです。 最初の1文字以降全て取得しようとしましたが、取得する文字数が全文字数分の指定になっています。 開始位置が1つ後ろにずれるので、取得文字数も1つ少なく指定しなければいけません。 また、この場合であれば、第一引数のみの指定をすることにより、最後の文字列まで取得されます。


  • Date、その1
  • Dim d1 As Date = Date.MinValue
    d1 = d1.AddDays(-1)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: 加算または減算された値は表現できない DateTime になります。
    パラメーター名:value
       場所 System.DateTime.AddTicks(Int64 value)
       場所 System.DateTime.Add(Double value, Int32 scale)
       場所 System.DateTime.AddDays(Double value)
    
    ' 対処方法
    Dim d1 As Date = Date.MinValue
    If Date.Compare(d1, Date.MinValue) = -1 Then
        d1 = d1.AddDays(-1)
    End If
    
  •  まず最初に、日付型について、扱える日付(境界値)を確認して、その範囲内で指定します。


  • Date、その2
  • Dim d1 As Date = Date.MaxValue
    d1 = d1.AddDays(1)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: 加算または減算された値は表現できない DateTime になります。
    パラメーター名:value
       場所 System.DateTime.AddTicks(Int64 value)
       場所 System.DateTime.Add(Double value, Int32 scale)
       場所 System.DateTime.AddDays(Double value)
    
    ' 対処方法
    前の対処方法と同様
    
  •  前の対処方法と同様、まず最初に、日付型について、扱える日付(境界値)を確認して、その範囲内で指定します。 年、月、日、それぞれ存在する値にします。うるう年に注意が必要です。


  • Date、その3
  • Dim d1 As New Date(-1, 1, 1)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: Year、Month および Day パラメーターが表現できない DateTime を示しています。
       場所 System.DateTime.DateToTicks(Int32 year, Int32 month, Int32 day)
       場所 System.DateTime..ctor(Int32 year, Int32 month, Int32 day)
    
    ' 対処方法
    前の対処方法と同様
    
  •  日付型のインスタンス生成時も、前の対処方法と同様です。


  • DateTime
  • Dim d1 As New DateTime(2014, 12, 31, 25, 0, 0)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: Hour、Minute および Second パラメーターが表現できない DateTime を示しています。
       場所 System.DateTime.TimeToTicks(Int32 hour, Int32 minute, Int32 second)
       場所 System.DateTime..ctor(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second)
    
    ' 対処方法
    前の対処方法と同様
    
  •  日付・時刻型のインスタンス生成時も、前の対処方法と同様です。


  • ループ中のコレクションデータ
  • ' ループ中のコレクションデータを削除する
    Dim lst As New List(Of Integer)(New Integer() {1, 2, 3})
    For i As Integer = 0 To lst.Count - 1
        lst.RemoveAt(i)
    Next
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
    パラメーター名:index
       場所 System.ThrowHelper.ThrowArgumentOutOfRangeException()
       場所 System.Collections.Generic.List`1.RemoveAt(Int32 index)
    
    ' 対処方法
    ループ中のコレクションデータに対して、追加、削除、挿入は実施しない
    または、
    Dim lst As New List(Of Integer)(New Integer() {1, 2, 3})
    Dim workList = lst.ToList()
    For i As Integer = 0 To workList.Count - 1
        lst.RemoveAt(i)
    Next
    
  •  この例外エラーは、削除の途中で発生します。1回目(i=0)で、lst(0)、1という値 を削除します。 すると残りは、lst(0~1)の2つ分になります。2回目(i=1)で、lst(1)、3という値 を削除します。 すると残りは、lst(0)の1つ分になります。3回目(i=2)で、lst(2) を参照しようとしますが、リストの個数は1つしかなく、 3個目は存在しないので、例外エラーが発生します。
  •  ループ中のコレクションデータに対して、編集を実施するような実装は避けるべきです。 2度手間に思えるかもしれませんが、別のコレクションデータを準備して、編集処理を行うことで例外エラーを回避することができます。


  • List(Of Integer) のインデックス指定
  • Dim lst As New List(Of Integer)(New Integer() {1, 2, 3})
    Dim i = lst(3)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
    パラメーター名:index
       場所 System.ThrowHelper.ThrowArgumentOutOfRangeException()
       場所 System.Collections.Generic.List`1.get_Item(Int32 index)
    
    ' 対処方法
    0 始まりに気を付け、存在する範囲のインデックスを指定する
    
  •  配列やリストに登録した各値を取得してくる場合、インデックス値は 0 始まりで指定します。 例えば、1つ目に登録した値は lst(0)、2つ目に登録した値は lst(1)、・・・、N個目に登録した値は lst(N-1) という風に指定します。 ※ lst は、リスト変数と仮定しています。


  • ListBox のインデックス指定
  • ' (登録データが無い状態で)
    Me.ListBox1.SelectedIndex = 0
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: '0' の InvalidArgument=Value は 'SelectedIndex' に対して有効ではありません。
    パラメーター名:SelectedIndex
       場所 System.Windows.Forms.ListBox.set_SelectedIndex(Int32 value)
    
    ' 対処方法
    If 0 < Me.ListBox1.Items.Count Then
        Me.ListBox1.SelectedIndex = 0
    End If
    
  •  インデックス値の注意点の内容も含みますが、別解として、事前に登録数を確認しています。 データ未登録の他、登録数を超えた値の指定にも注意してください。


  • TreeView のインデックス指定
  • ' (登録データが無い状態で)
    Me.TreeView1.SelectedNode = Me.TreeView1.Nodes(0)
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: 指定された引数は、有効な値の範囲内にありません。
    パラメーター名:index
       場所 System.Windows.Forms.TreeNodeCollection.get_Item(Int32 index)
    
    ' 対処方法
    前の対処方法と同様(登録数の確認)
    
  •  同様に、事前に登録数を確認しています。 前の処理で、いったんデータクリア処理をして、そのままの状態になっていないかも注意してください。


  • ComboBox のインデックス指定
  • ' (登録データが無い状態で)
    Me.ComboBox1.SelectedIndex = 0
    
    ' 例外エラー
    System.ArgumentOutOfRangeException: '0' の InvalidArgument=Value は 'SelectedIndex' に対して有効ではありません。
    パラメーター名:SelectedIndex
       場所 System.Windows.Forms.ComboBox.set_SelectedIndex(Int32 value)
    
    ' 対処方法
    前の対処方法と同様(登録数の確認)
    
  •  同様に、事前に登録数を確認します。

  •  最後までこの記事を読んでいただき、ありがとうございました。

  • スポンサーリンク