IndexOutOfRangeException / ArgumentOutOfRangeException은 무엇이며 어떻게 해결합니까?
나는 약간의 코드를 가지고 있고 그것이 실행될 때 IndexOutOfRangeException
,
인덱스 배열의 범위를 벗어난 것입니다.
이것이 의미하는 바는 무엇이며 어떻게해야합니까?
사용되는 클래스에 따라 ArgumentOutOfRangeException
mscorlib.dll에서 'System.ArgumentOutOfRangeException'유형의 예외가 발생했지만 사용자 코드에서 처리되지 않았습니다. 추가 정보 : 인덱스가 범위를 벗어났습니다. 음수가 아니고 컬렉션의 크기보다 작아야합니다.
답변
그것은 무엇입니까?
이 예외는 잘못된 인덱스를 사용하여 인덱스별로 컬렉션 항목에 액세스하려고한다는 것을 의미합니다. 인덱스가 컬렉션의 하한보다 낮거나 포함 된 요소의 수보다 크거나 같으면 인덱스가 유효하지 않습니다.
던져 질 때
다음과 같이 선언 된 배열이 주어집니다.
byte[] array = new byte[4];
0에서 3까지이 배열에 액세스 IndexOutOfRangeException
할 수 있으며이 범위를 벗어난 값은 throw됩니다. 배열을 만들고 액세스 할 때 이것을 기억하십시오.
배열 길이
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을 반환합니다 (nullables는 .NET 2.0에 도입되었으며 어쨌든 수년 동안 사용되는 잘 알려진 규칙이기도합니다). 아무것도 찾을 수 없습니다. 문자열과 비교할 수있는 객체 배열이 있다고 가정 해 보겠습니다. 다음 코드를 작성하는 것이 좋습니다.
// 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
당신이 데이터 세트는 두 개의 열을 가지고 있지만 당신이 3 일에서 값을 얻기 위해 노력하고이기 때문에 (인덱스는 항상 0 기반).
이 동작은 대부분의 IDataReader
구현 ( SqlDataReader
등) OleDbDataReader
과 공유됩니다 .
열 이름을 사용하고 잘못된 열 이름을 전달하는 인덱서 연산자의 IDataReader 오버로드를 사용하는 경우에도 동일한 예외가 발생할 수 있습니다.
예를 들어 Column1 이라는 열 을 검색했지만 다음을 사용하여 해당 필드의 값을 검색하려고 한다고 가정합니다.
var data = dr["Colum1"]; // Missing the n in Column1.
이는 존재하지 않는 Colum1 필드 의 인덱스를 검색하려는 인덱서 연산자가 구현 되었기 때문에 발생 합니다. GetOrdinal 메서드는 내부 도우미 코드가 "Colum1"의 인덱스로 -1을 반환 할 때이 예외를 throw합니다.
기타이
예외가 발생하는 또 다른 (문서화 된) 경우 가 있습니다. 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) { }
이것이 허용되지 않는다는 점에 유의하십시오 (throw됩니다 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];
}
}
반환 값 유효성 검사
이전 예제 중 하나에서 반환 값 을 직접 사용했습니다 Array.IndexOf()
. 실패 할 수 있다는 것을 알고 있다면 해당 케이스를 처리하는 것이 좋습니다.
int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
디버깅 방법
제 생각에는이 오류에 대한 대부분의 질문은 간단히 피할 수 있습니다. 적절한 질문을 작성하는 데 소요되는 시간 (작은 작업 예제와 간단한 설명 포함)은 코드를 디버그하는 데 필요한 시간보다 훨씬 더 많을 수 있습니다. 우선, 작은 프로그램의 디버깅에 대한 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 규칙
이 예외는 잘못된 인덱스를 사용하여 인덱스별로 컬렉션 항목에 액세스하려고한다는 것을 의미합니다. 컬렉션의 하한보다 낮거나 더 큰 인덱스는 유효하지 않습니다.포함 된 요소의 수와 같습니다. 배열 선언에 정의 된 최대 허용 인덱스
Index out of bound 예외가 무엇인지에 대한 간단한 설명 :
기차 한 대가 거기에 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 루프는 4 번 반복됩니다 (0, 1, 2 및 3).
따라서 (3)을 사용하여 경계 외부에 액세스하려고하면 예외가 발생합니다.
매우 긴 완전한 허용 답변의 측면에는 IndexOutOfRangeException
다른 많은 예외 유형과 비교할 때 중요한 점이 있습니다.
종종 코드의 특정 지점에서 제어하기 어려울 수있는 복잡한 프로그램 상태가 있습니다. 예를 들어 DB 연결이 중단되어 입력에 대한 데이터를 검색 할 수 없습니다. 그것이 발생하는 곳은 그 시점에서 그것을 처리 할 방법이 없기 때문입니다.
IndexOutOfRangeException
대부분의 경우 예외가 발생하는 지점에서 확인하는 것이 매우 간단하다는 점에서 일반적으로 다릅니다. 일반적으로 이러한 종류의 예외는 발생하는 위치에서 문제를 매우 쉽게 처리 할 수있는 일부 코드에서 발생합니다. 배열의 실제 길이를 확인하는 것입니다. 이 예외를 더 높은 수준으로 처리하여 '수정'하고 싶지는 않지만 대신 첫 번째 인스턴스에서 발생하지 않도록 보장하여 대부분의 경우 배열 길이를 확인하여 쉽게 수행 할 수 있습니다.
이것을 넣는 또 다른 방법은 입력 또는 프로그램 상태에 대한 진정한 제어 부족으로 인해 다른 예외가 발생할 수 있지만 IndexOutOfRangeException
단순히 파일럿 (프로그래머) 오류가 아닌 경우가 더 많다는 것입니다.