무엇입니까?
이 예외는 잘못된 인덱스를 사용하여 인덱스로 컬렉션 항목에 액세스하려고 함을 의미합니다. 컬렉션의 하한보다 작거나 포함 된 요소 수보다 크거나 같은 인덱스는 유효하지 않습니다.
던져 질 때
다음과 같이 선언 된 배열이 주어집니다.
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을 반환합니다 (.NET 2.0에서 nullables가 도입되었으며 어쨌든 수년 동안 잘 알려진 규칙 임). 아무것도 찾을 수 없습니다. 문자열과 비슷한 객체 배열이 있다고 가정 해 봅시다. 이 코드를 작성한다고 생각할 수도 있습니다.
// 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
데이터 세트에 열이 두 개뿐 이므로 세 번째 열에서 값을 얻으려고 시도합니다 (표시는 항상 0 기반 임).
이 동작은 대부분의 IDataReader
구현 과 공유됩니다 ( SqlDataReader
등 OleDbDataReader
).
열 이름을 사용하고 유효하지 않은 열 이름을 전달하는 인덱서 연산자의 IDataReader 오버로드를 사용하는 경우에도 동일한 예외가 발생할 수 있습니다.
예를 들어, Column1 이라는 열 을 검색했지만 다음을 사용하여 해당 필드의 값을 검색하려고 한다고 가정하십시오.
var data = dr["Colum1"]; // Missing the n in Column1.
이는 존재하지 않는 Colum1 필드 의 색인을 검색하려고 인덱서 연산자가 구현 되었기 때문에 발생 합니다. 내부 도우미 코드에서 -1을 "Colum1"의 인덱스로 반환하면 GetOrdinal 메서드에서이 예외가 발생합니다.
기타이
예외가 발생하는 경우 (문서화 된) 또 다른 경우가 있습니다. in 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);
}
함수가 private 인 경우 단순히 if
논리를 Debug.Assert()
다음 과 같이 대체 할 수 있습니다 .
Debug.Assert(from >= 0 && from < array.Length);
Check Object State
Array 색인이 매개 변수에서 직접 제공되지 않을 수 있습니다. 객체 상태의 일부일 수 있습니다. 일반적으로 객체 상태를 자체적으로 (필요한 경우 함수 매개 변수를 사용하여) 확인하는 것이 좋습니다. 을 사용하거나 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];
}
}
반환 값 확인
이전 예제 중 하나에서 반환 값 을 직접 사용했습니다 Array.IndexOf()
. 우리가 실패 할 수 있다는 것을 안다면, 그 사건을 다루는 것이 좋습니다.
int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
디버깅하는 방법
제 생각에,이 오류에 관한 대부분의 질문은 간단하게 피할 수 있습니다. 적절한 질문을 작성하는 데 소요되는 시간 (작은 실례와 작은 설명으로)은 코드를 디버깅하는 데 필요한 시간보다 훨씬 더 많을 수 있습니다. 우선, 작은 프로그램의 디버깅에 관한 Eric Lippert의 블로그 게시물을 읽으십시오 . 여기서 그의 말을 반복하지는 않지만 반드시 읽어야 합니다.
소스 코드가 있고 스택 추적이 포함 된 예외 메시지가 있습니다. 거기에 가서 올바른 줄 번호를 선택하면 다음을 볼 수 있습니다.
array[index] = newValue;
당신은 당신의 오류를 발견 어떻게 index
증가 하는지 확인하십시오 . 맞아? 배열이 어떻게 할당되는지 확인하고 어떻게 index
증가 하는지 일관성 사양에 따라 맞습니까? 이 모든 질문에 예라고 대답 하면 StackOverflow에 대한 유용한 도움말을 찾을 수 있지만 먼저 직접 확인하십시오. 당신은 당신의 자신의 시간을 절약 할 수 있습니다!
좋은 시작점은 항상 어설 션을 사용하고 입력을 확인하는 것입니다. 코드 계약을 사용할 수도 있습니다. 문제가 발생하여 코드를 빠르게 살펴보면 어떤 일이 발생하는지 파악할 수 없으면 옛 친구 인 debugger 를 사용해야합니다 . 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 규칙
이 예외는 잘못된 인덱스를 사용하여 인덱스로 컬렉션 항목에 액세스하려고 함을 의미합니다. 컬렉션의 하한보다 작거나보다 큰 인덱스는 유효하지 않습니다.포함하는 요소 수와 같습니다. 배열 선언에 정의 된 최대 허용 인덱스