코드가 있고 실행될 때 IndexOutOfRangeException
,
인덱스 배열의 범위를 벗어난 것입니다.
이것은 무엇을 의미하며 이에 대해 무엇을 할 수 있습니까?
사용되는 클래스에 따라 ArgumentOutOfRangeException
mscorlib.dll에서 ‘System.ArgumentOutOfRangeException’유형의 예외가 발생했지만 사용자 코드에서 처리되지 않았습니다. 추가 정보 : 색인이 범위를 벗어났습니다. 음수가 아니어야하며 컬렉션 크기보다 작아야합니다.
답변
무엇입니까?
이 예외는 잘못된 인덱스를 사용하여 인덱스로 컬렉션 항목에 액세스하려고 함을 의미합니다. 컬렉션의 하한보다 작거나 포함 된 요소 수보다 크거나 같은 인덱스는 유효하지 않습니다.
던져 질 때
다음과 같이 선언 된 배열이 주어집니다.
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 규칙
이 예외는 잘못된 인덱스를 사용하여 인덱스로 컬렉션 항목에 액세스하려고 함을 의미합니다. 컬렉션의 하한보다 작거나보다 큰 인덱스는 유효하지 않습니다.포함하는 요소 수와 같습니다. 배열 선언에 정의 된 최대 허용 인덱스
답변
Index out of bound exception이 무엇인지에 대한 간단한 설명 :
한 열차에 구획이 D1, D2, D3이라고 생각하십시오. 한 승객이 기차에 들어 와서 D4 티켓을 받았습니다. 이제 어떻게 될까요. 승객이 존재하지 않는 구획에 들어가기를 원하므로 분명히 문제가 발생합니다.
같은 시나리오 : 배열 목록 등에 액세스하려고 할 때마다 배열의 기존 인덱스에만 액세스 할 수 있습니다. array[0]
그리고 array[1]
존재합니다. 우리가 액세스하려고하면 array[3]
실제로 존재하지 않으므로 바운드 예외 인덱스가 발생합니다.
답변
문제를 쉽게 이해하기 위해 다음 코드를 작성했다고 상상해보십시오.
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-loop는 4 번 (0, 1, 2 및 3) 루프됩니다.
따라서 (3)으로 경계 외부에 액세스하려고하면 예외가 발생합니다.
답변
매우 오랫동안 받아 들여진 대답의 한 측면에서 IndexOutOfRangeException
다른 많은 예외 유형과 비교할 때 중요한 점이 있습니다.
종종 코드의 특정 지점에서 제어하기 어려운 복잡한 프로그램 상태가 있습니다. 예를 들어 DB 연결이 끊어 지므로 입력 데이터를 검색 할 수 없습니다. 이런 종류의 문제로 인해 종종 그것이 발생하는 곳은 그 시점에서 그것을 다루는 방법이 없기 때문에 더 높은 수준으로 버블 링해야합니다.
IndexOutOfRangeException
대부분의 경우 예외가 발생하는 시점에서 확인하는 것이 매우 간단하다는 점에서 일반적으로 다릅니다. 일반적으로 이러한 종류의 예외는 발생하는 장소에서 문제를 매우 쉽게 처리 할 수있는 일부 코드에 의해 발생합니다. 배열의 실제 길이를 확인하면됩니다. 이 예외를 더 높게 처리하여이를 ‘수정’하고 싶지는 않지만 대신 첫 번째 인스턴스에서 발생하지 않도록함으로써 대부분의 경우 배열 길이를 확인하여 쉽게 수행 할 수 있습니다.
이것을 넣는 또 다른 방법 IndexOutOfRangeException
은 단순히 파일럿 (프로그래머) 오류가 아니라 입력 또는 프로그램 상태 BUT에 대한 진정한 제어 부족으로 인해 다른 예외가 발생할 수 있다는 것입니다.