c#
#c# #.net #исключение indexoutofrangeexception
Вопрос:
У меня есть некоторый код, и когда он выполняется, он выдает IndexOutOfRangeException
сообщение,
Индекс находился за пределами массива.
Что это значит, и что я могу с этим поделать?
В зависимости от используемых классов это также может быть ArgumentOutOfRangeException
Исключение типа ‘System.Исключение ArgumentOutOfRangeException’ произошло в mscorlib.dll но не был обработан в пользовательском коде Дополнительная информация: индекс был вне диапазона. Должно быть неотрицательным и меньше размера коллекции.
Комментарии:
1. В вашей коллекции, если у вас всего 4 элемента, но код пытался получить элемент в индексе 5. Это вызовет исключение IndexOutOfRangeException. Проверьте index = 5; если (элементы. Консоль Length >= index ). Строка записи (вводит [индекс]);
Ответ №1:
Что это такое?
Это исключение означает, что вы пытаетесь получить доступ к элементу коллекции по индексу, используя недопустимый индекс. Индекс недействителен, если он ниже нижней границы коллекции или больше или равен количеству элементов, которые он содержит.
Когда оно выдается
Учитывая массив, объявленный как:
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;
}
}
Верхняя граница не включена
В следующем примере мы создаем необработанный двумерный массив 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
классе).
Arrays Do Not Grow
An array is fast. Very fast in linear search compared to every other collection. It is because items are contiguous in memory so memory address can be calculated (and increment is just an addition). No need to follow a node list, simple math! You pay this with a limitation: they can’t grow, if you need more elements you need to reallocate that array (this may take a relatively long time if old items must be copied to a new block). You resize them with Array.Resize<T>()
, this example adds a new entry to an existing array:
Array.Resize(ref array, array.Length 1);
Don’t forget that valid indices are from 0
to Length - 1
. If you simply try to assign an item at Length
you’ll get IndexOutOfRangeException
(this behavior may confuse you if you think they may increase with a syntax similar to Insert
method of other collections).
Special Arrays With Custom Lower Bound
First item in arrays has always index 0. This is not always true because you can create an array with a custom lower bound:
var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });
In that example, array indices are valid from 1 to 4. Of course, upper bound cannot be changed.
Wrong Arguments
If you access an array using unvalidated arguments (from user input or from function user) you may get this error:
private static string[] RomanNumbers =
new string[] { "I", "II", "III", "IV", "V" };
public static string Romanize(int number)
{
return RomanNumbers[number];
}
Unexpected Results
This exception may be thrown for another reason too: by convention, many search functions will return -1 (nullables has been introduced with .NET 2.0 and anyway it’s also a well-known convention in use from many years) if they didn’t find anything. Let’s imagine you have an array of objects comparable with a string. You may think to write this code:
// 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")]);
Это завершится неудачей, если никакие элементы в myArray
не будут удовлетворять условию поиска, потому Array.IndexOf()
что вернет -1, а затем будет выдан доступ к массиву.
Следующий пример — наивный пример для вычисления вхождений заданного набора чисел (зная максимальное число и возвращая массив, где элемент с индексом 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 resu<
}
Конечно, это довольно ужасная реализация, но я хочу показать, что она не будет работать для отрицательных чисел и чисел выше maximum
.
К чему это относится List<T>
?
Те же случаи, что и в массиве — диапазон допустимых индексов — 0 ( List
индексы всегда начинаются с 0) list.Count
— доступ к элементам за пределами этого диапазона вызовет исключение.
Обратите внимание, что List<T>
выдает ArgumentOutOfRangeException
для тех же случаев, когда используются массивы IndexOutOfRangeException
.
В отличие от массивов, List<T>
starts empty — поэтому попытка доступа к элементам только что созданного списка приводит к этому исключению.
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 выдаст это исключение, когда его внутренний вспомогательный код вернет значение -1 в качестве индекса «Colum1».
Прочее
Существует еще один (документированный) случай, когда возникает это исключение: если в DataView
, имя столбца данных, предоставляемое DataViewSort
свойству, недопустимо.
Как избежать
В этом примере позвольте мне для простоты предположить, что массивы всегда одномерны и основаны на 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);
}
If function is private you may simply replace if
logic with Debug.Assert()
:
Debug.Assert(from >= 0 amp;amp; from < array.Length);
Check Object State
Array index may not come directly from a parameter. It may be part of object state. In general is always a good practice to validate object state (by itself and with function parameters, if needed). You can use Debug.Assert()
, throw a proper exception (more descriptive about the problem) or handle that like in this example:
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];
}
}
Validate Return Values
In one of previous examples we directly used Array.IndexOf()
return value. If we know it may fail then it’s better to handle that case:
int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
How to Debug
In my opinion, most of the questions, here on SO, about this error can be simply avoided. The time you spend to write a proper question (with a small working example and a small explanation) could easily much more than the time you’ll need to debug your code. First of all, read this Eric Lippert’s blog post about debugging of small programs, I won’t repeat his words here but it’s absolutely a must read.
You have source code, you have exception message with a stack trace. Go there, pick right line number and you’ll see:
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
Таким образом, этот цикл заполнит массив 5 целыми числами, не вызывая никакого исключения IndexOutOfRangeException
For i As Integer = 0 To 4
myArray(i) = i
Next
VB.NET правило
Это исключение означает, что вы пытаетесь получить доступ к элементу коллекции по индексу, используя недопустимый индекс. Индекс недействителен, если он ниже нижней границы коллекции или больше, чем равно количеству элементов, которые он содержит.максимально допустимый индекс, определенный в объявлении массива
Ответ №2:
Простое объяснение того, что такое исключение Index out of bound:
Просто подумайте, что есть один поезд, в котором есть отсеки D1, D2, D3. Один пассажир пришел, чтобы войти в поезд, и у него есть билет на D4. что теперь произойдет. пассажир хочет войти в отсек, которого не существует, поэтому, очевидно, возникнет проблема.
Тот же сценарий: всякий раз, когда мы пытаемся получить доступ к списку массивов и т. Д. мы можем получить доступ только к существующим индексам в массиве. array[0]
и array[1]
существуют. Если мы попытаемся получить доступ array[3]
, на самом деле его там нет, поэтому возникнет исключение index out of bound.
Ответ №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), он выдает исключение.
Ответ №4:
В стороне от очень длинного полного принятого ответа есть важный момент, который следует учитывать по IndexOutOfRangeException
сравнению со многими другими типами исключений, и это:
Часто возникает сложное состояние программы, которое, возможно, трудно контролировать в определенной точке кода, например, соединение с БД прерывается, поэтому данные для ввода не могут быть получены и т.д… Такого рода проблемы часто приводят к некоторому исключению, которое должно всплывать на более высокий уровень, потому что там, где это происходит, нет способа справиться с этим в этот момент.
IndexOutOfRangeException
обычно отличается тем, что в большинстве случаев довольно тривиально проверять в точке, где возникает исключение. Обычно такого рода исключения генерируются некоторым кодом, который может очень легко решить проблему в том месте, где она возникает — просто путем проверки фактической длины массива. Вы не хотите «исправлять» это, обрабатывая это исключение выше, но вместо этого гарантируя, что оно не будет выдано в первом экземпляре, что в большинстве случаев легко сделать, проверив длину массива.
Другой способ выразить это заключается в том, что другие исключения могут возникать из-за подлинного отсутствия контроля над вводом или состоянием программы, НО IndexOutOfRangeException
чаще всего это просто ошибка пилота (программиста).
Ответ №5:
Эти два исключения распространены в различных языках программирования, и, как говорили другие, это когда вы обращаетесь к элементу с индексом, превышающим размер массива. Например:
var array = [1,2,3];
/* var lastElement = array[3] this will throw an exception, because indices
start from zero, length of the array is 3, but its last index is 2. */
Основная причина этого заключается в том, что компиляторы обычно не проверяют этот материал, поэтому они будут проявляться только во время выполнения.
Комментарии:
1. Если размер массива и / или индекс, к которому осуществляется доступ, неизвестны до времени выполнения, тогда компилятор не может проверить, что индекс действителен. В противном случае это не добавляет никакой информации, которая не охвачена другими ответами.
2. Да, но в случае известного размера массива компилятор также ничего не делает. Я полагаю, по крайней мере, в C , Java и C #.