Что такое IndexOutOfRangeException / ArgumentOutOfRangeException и как это исправить?



у меня есть некоторый код, и когда он выполняет, он бросает!--0-->, говорит:




индекс находился за пределами массива.




что это значит, и что я могу поделать?



в зависимости от используемых классов он также может быть ArgumentOutOfRangeException




исключение типа System.ArgumentOutOfRangeException ' произошло в mscorlib.DLL но не было обработано в коде пользователя дополнительные сведения: индекс находился вне допустимого диапазона. Должен быть неотрицательным и меньшим, чем размер коллекции.


832   3  

3 ответов:

Что Это?

это исключение означает, что вы пытаетесь получить доступ к элементу коллекции по индексу, используя недопустимый индекс. Индекс недопустим, если он ниже нижней границы коллекции или больше или равен количеству элементов, которые он содержит.

Когда Он Брошен

учитывая массив, объявленный как:

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 класс).

Массивы Не Растут
Массив-это быстро. Очень быстро в линейном поиске по сравнению с любой другой коллекцией. Это связано с тем, что элементы являются смежными в памяти, поэтому адрес памяти может быть вычислен (а инкремент-это просто дополнение). Не нужно следовать списку узлов, простая математика! Вы платите это с ограничением: они не могут расти, если вам нужно больше элементов, вам нужно перераспределить этот массив (это может быть расширено, если старые элементы должны будет скопирован в новый блок). Вы изменяете их размер с помощью 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. Конечно, верхняя граница не может быть изменена.

Неправильные Аргументы
Если вы обращаетесь к массиву с помощью непроверенных аргументов (из пользовательского ввода или из функции user), вы можете получить это ошибка:

private static string[] RomanNumbers =
    new string[] { "I", "II", "III", "IV", "V" };

public static string Romanize(int number)
{
    return RomanNumbers[number];
}

Неожиданные Результаты
Это исключение может быть сделано и по другой причине: по соглашению many поиск функции вернет -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")]);

это не удастся если нет элементов 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 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 потому что у вас dataset есть только два столбца, но вы пытаетесь получить значение из 3-го (индексы всегда 0-based).

обратите внимание, что это поведение разделяется с большинством IDataReader реализаций (SqlDataReader,OleDbDataReader и так далее).

такое же исключение можно получить и при использовании перегрузки IDataReader оператора индексатора, который принимает имя столбца и передает недопустимое имя столбца.
Предположим, например, что вы получили столбец с именем Column1 но тогда вы пытаетесь получить значение этого поля с

 var data = dr["Colum1"];  // Missing the n in Column1.

это происходит потому, что оператор индексатора реализуется, пытаясь получить индекс a Colum1 поле, которое не существует. Метод GetOrdinal вызовет это исключение, когда его внутренний вспомогательный код возвращает значение -1 в качестве индекса "Colum1".

другие
Есть еще один (документированный) случай, когда это исключение выдается, если, в DataView имя столбца данных к DataViewSort собственность не действительный.

Как избежать

в этих примерах позвольте мне для простоты предположить, что массивы всегда одномерны и основаны на 0. Если вы хотите быть строгим (или разрабатываете библиотеку), вам может потребоваться заменить 0 С GetLowerBound(0) и .Length С GetUpperBound(0) (конечно, если у вас есть параметры типа System.Array, это не относится к 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 логика с 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 { }

отладка

на мой взгляд, большинство вопросов здесь, на так об этой ошибке можно просто избежать. Время, которое вы тратите на написать правильный вопрос (с небольшим рабочим примером и небольшим объяснением) может легко гораздо больше, чем время, необходимое для отладки кода. Прежде всего, прочитайте это сообщение в блоге Эрика Липперта о отладки небольших программ, я не буду повторять его слова здесь, но это абсолютно a следует читать.

у вас есть исходный код, у вас есть сообщение об исключении с трассировкой стека. Идите туда, выберите правильный номер строки, и вы увидите:

array[index] = newValue;

вы нашел свою ошибку, проверьте как index увеличивается. Это правильно? Проверьте, как массив выделяется, согласован с тем, как index увеличивается? Это правильно в соответствии с вашей спецификацией? Если вы ответите да на все эти вопросы, то вы найдете хорошую помощь здесь на StackOverflow, но, пожалуйста, сначала проверьте это самостоятельно. Вы сэкономите свое время!

хорошей отправной точкой является всегда использовать утверждения и проверять входные данные. Вы даже можете использовать контракты кода. Когда что-то пошло не так, и вы не можете понять, что происходит с быстрым взглядом на ваш код, то вы должны прибегнуть к старому другу: отладчик. Просто запустите приложение в debug внутри Visual Studio (или вашей любимой IDE), вы увидите, какая именно строка вызывает это исключение, какой массив задействован и какой индекс вы пытаетесь использовать. Действительно, в 99% случаев вы решите это самостоятельно за несколько минут.

если это происходит в производстве, то вам лучше добавить утверждения в инкриминируемом коде, вероятно, мы не увидим в вашем коде то, что вы не можете увидеть сами (но вы всегда можете сделать ставку).

простое объяснение того, что такое индекс из связанного исключения:

просто думаю, что один поезд там его отсеки 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), но при попытке доступа за пределы границ с (3) он выдает исключение.

Comments

    Ничего не найдено.