System indexoutofrangeexception как исправить

What Is It?

This exception means that you’re trying to access a collection item by index, using an invalid index. An index is invalid when it’s lower than the collection’s lower bound or greater than or equal to the number of elements it contains.

When It Is Thrown

Given an array declared as:

byte[] array = new byte[4];

You can access this array from 0 to 3, values outside this range will cause IndexOutOfRangeException to be thrown. Remember this when you create and access an array.

Array Length
In C#, usually, arrays are 0-based. It means that first element has index 0 and last element has index Length - 1 (where Length is total number of items in the array) so this code doesn’t work:

array[array.Length] = 0;

Moreover please note that if you have a multidimensional array then you can’t use Array.Length for both dimension, you have to use 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;
    }
}

Upper Bound Is Not Inclusive
In the following example we create a raw bidimensional array of Color. Each item represents a pixel, indices are from (0, 0) to (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;
    }
}

This code will then fail because array is 0-based and last (bottom-right) pixel in the image is pixels[imageWidth - 1, imageHeight - 1]:

pixels[imageWidth, imageHeight] = Color.Black;

In another scenario you may get ArgumentOutOfRangeException for this code (for example if you’re using GetPixel method on a Bitmap class).

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")]);

This will fail if no items in myArray will satisfy search condition because Array.IndexOf() will return -1 and then array access will throw.

Next example is a naive example to calculate occurrences of a given set of numbers (knowing maximum number and returning an array where item at index 0 represents number 0, items at index 1 represents number 1 and so on):

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;
}

Of course, it’s a pretty terrible implementation but what I want to show is that it’ll fail for negative numbers and numbers above maximum.

How it applies to List<T>?

Same cases as array — range of valid indexes — 0 (List‘s indexes always start with 0) to list.Count — accessing elements outside of this range will cause the exception.

Note that List<T> throws ArgumentOutOfRangeException for the same cases where arrays use IndexOutOfRangeException.

Unlike arrays, List<T> starts empty — so trying to access items of just created list lead to this exception.

var list = new List<int>();

Common case is to populate list with indexing (similar to Dictionary<int, T>) will cause exception:

list[0] = 42; // exception
list.Add(42); // correct

IDataReader and Columns
Imagine you’re trying to read data from a database with this code:

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() will throw IndexOutOfRangeException because you’re dataset has only two columns but you’re trying to get a value from 3rd one (indices are always 0-based).

Please note that this behavior is shared with most IDataReader implementations (SqlDataReader, OleDbDataReader and so on).

You can get the same exception also if you use the IDataReader overload of the indexer operator that takes a column name and pass an invalid column name.
Suppose for example that you have retrieved a column named Column1 but then you try to retrieve the value of that field with

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

This happens because the indexer operator is implemented trying to retrieve the index of a Colum1 field that doesn’t exist. The GetOrdinal method will throw this exception when its internal helper code returns a -1 as the index of «Colum1».

Others
There is another (documented) case when this exception is thrown: if, in DataView, data column name being supplied to the DataViewSort property is not valid.

How to Avoid

In this example, let me assume, for simplicity, that arrays are always monodimensional and 0-based. If you want to be strict (or you’re developing a library), you may need to replace 0 with GetLowerBound(0) and .Length with GetUpperBound(0) (of course if you have parameters of type System.Array, it doesn’t apply for T[]). Please note that in this case, upper bound is inclusive then this code:

for (int i=0; i < array.Length; ++i) { }

Should be rewritten like this:

for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) { }

Please note that this is not allowed (it’ll throw InvalidCastException), that’s why if your parameters are T[] you’re safe about custom lower bound arrays:

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 }));
}

Validate Parameters
If index comes from a parameter you should always validate them (throwing appropriate ArgumentException or ArgumentOutOfRangeException). In the next example, wrong parameters may cause IndexOutOfRangeException, users of this function may expect this because they’re passing an array but it’s not always so obvious. I’d suggest to always validate parameters for public functions:

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 && 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;

You found your error, check how index increases. Is it right? Check how array is allocated, is coherent with how index increases? Is it right according to your specifications? If you answer yes to all these questions, then you’ll find good help here on StackOverflow but please first check for that by yourself. You’ll save your own time!

A good start point is to always use assertions and to validate inputs. You may even want to use code contracts. When something went wrong and you can’t figure out what happens with a quick look at your code then you have to resort to an old friend: debugger. Just run your application in debug inside Visual Studio (or your favorite IDE), you’ll see exactly which line throws this exception, which array is involved and which index you’re trying to use. Really, 99% of the times you’ll solve it by yourself in a few minutes.

If this happens in production then you’d better to add assertions in incriminated code, probably we won’t see in your code what you can’t see by yourself (but you can always bet).

The VB.NET side of the story

Everything that we have said in the C# answer is valid for VB.NET with the obvious syntax differences but there is an important point to consider when you deal with VB.NET arrays.

In VB.NET, arrays are declared setting the maximum valid index value for the array. It is not the count of the elements that we want to store in the array.

' declares an array with space for 5 integer 
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer

So this loop will fill the array with 5 integers without causing any IndexOutOfRangeException

For i As Integer = 0 To 4
    myArray(i) = i
Next

The VB.NET rule

This exception means that you’re trying to access a collection item by index, using an invalid index. An index is invalid when it’s lower than the collection’s lower bound or greater than equal to the number of elements it contains. the maximum allowed index defined in the array declaration

System.IndexOutOfRangeException: «Индекс находился вне границ массива.»

using System;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace Project10
{
    class Class1
    {
        public static void Encrypte()
        {
            int p = 0;
            int d = 0;
            StreamReader rdr = new StreamReader(@"UsersDesktop1.txt");
            string text = Regex.Replace(rdr.ReadToEnd().Trim(), "\s+", " ");
            StreamReader message = new StreamReader(@"UsersDesktopmessage.txt");
            string writePath = @"UsersDesktop2.txt";
            string value = message.ReadToEnd();
            int sum = 0;
            byte[] asciiBytes = Encoding.GetEncoding(1251).GetBytes(value);
            string letter = "";
            string newText = "";
            int j = 0;
            for (int i = 0; i < text.Length; i++)
            {
                letter = Convert.ToString(text[i]);
                if (letter == " ")
                {
                    sum++;
                    if (p == 0)
                    {
                        d = asciiBytes[j]; //тут вот
                        j++;
                    }
                    if (d % 2 == 1)
                    {
                        letter = " ";
                        d /= 2;
                        p++;
                    }
                    else
                    {
                        letter = " ";
                        d /= 2;
                        p++;
                    }
                    if (p == 8) p = 0;
                }
                newText += letter;
                try
                {
                    using (StreamWriter sw = new StreamWriter(writePath, false, System.Text.Encoding.Default))
                    {
                        sw.Write(newText);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
            rdr.Close();
            Console.WriteLine("Пробелов: " + sum);
            Console.WriteLine("Зашифруется " + sum / 8 + " символов.");
        }
        public static void Decrypte()
        {
            StreamReader rdr = new StreamReader(@"/Users/Desktop/2.txt");
            string text = rdr.ReadToEnd();
            char[] array = text.ToCharArray();
            string result = "";
            for (int i = 1; i < array.Length; i++)
            {
                if (array[i] == ' ' & array[i] != array[i - 1])
                {
                    result += "0";
                }
                else if (array[i] == ' ' & array[i] == array[i - 1])
                {
                    result = result.Remove(result.Length - 1);
                    result += "1";
                }
            }
            Console.WriteLine();
            Console.WriteLine("Зашифрованое сообщение:");
            string neww = "";
            string message = "";
            for (int n = 0; n < result.Length; n++)
            {
                for (int i = 0; i < 8; i++)
                {
                    neww += result[i];
                }
                string output = new string(neww.ToCharArray().Reverse().ToArray());
                int dec = Convert.ToInt32(output, 2);
                byte[] two = new byte[1];
                two[0] = (byte)dec;
                String decodedString = Encoding.GetEncoding(1251).GetString(two);
                message += decodedString;
                result = result.Remove(0, 8);
                neww = "";
            }
            Console.WriteLine(message);
        }
        public static void Main(string[] args)
        {
            Encrypte();
            Decrypte();
        }

    }
}

In this article, we are going to learn about one of the most common exceptions in C# – IndexOutOfRangeException.

To download the source code for this article, you can visit our GitHub repository.

We’re going to learn about various situations where this exception gets raised and about the preventive measures we can take to make our applications more robust.

IndexOutOfRangeException gets thrown when we try to access a member of an array or a collection using an invalid index.

An invalid index means either the index is lower than the collection’s lower bound or greater than/equal to the number of elements in the collection.

We are going to cover some common examples of where IndexOutOfRangeException is raised.

Another exception you’re familiar with is NullReferenceException. If you want to learn more, we have an article about NullReferenceException as well.

Array – Upper Bound

The upper bound of a zero-based array – should be one less than the array length.

Let’s create an integer array and run a for loop on it to demonstrate the exception:

var numbersArray = new int[] { 1, 2, 3, 4, 5 };

for (var i = 0; i <= numbersArray.Length; i++)
{
    Console.WriteLine(numbersArray[i]);
}

The for loop runs perfectly fine when i < 5, but it fails for i == 5 because there is no element at the position 6 (index 5 – {0, 1, 2, 3, 4 , 5}):

1
2
3
4
5
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

How to Fix IndexOutOfRangeException?

Now, let’s see how we can fix IndexOutOfRangeException in this case.

Let’s remove the equality condition and just check for i < numbersArray.Length :

var numbersArray = new int[] { 1, 2, 3, 4, 5 };

for (var i = 0; i < numbersArray.Length; i++)
{
    Console.WriteLine(numbersArray[i]);
}

And if we run the application again, there’s no exception this time:

1
2
3
4
5

Array – Custom Lower Bound

We can create an array with a custom lower bound by using the Array.CreateInstance(Type elementType, int[] lengths, int[] lowerBounds) method:

var customLowerBoundArray = Array.CreateInstance(typeof(int), new int[] { 5 }, new int[] { 1 });
var value = 2;

for (var i = 0; i < customLowerBoundArray.Length; i++)
{
    customLowerBoundArray.SetValue(value, i);
    value *= 5;
}

The code snippet throws IndexOutOfRangeException when we try to set the array element’s value at an index lower than the custom lower bound.

The lower bound is 1, but the first element is set at position 0 (i == 0).

Solution

Let’s see how we can prevent the exception in this case:

var customLowerBoundArray = Array.CreateInstance(typeof(int), new int[] { 5 }, new int[] { 1 });
var value = 2;
 
for (var i = customLowerBoundArray.GetLowerBound(0); i <= customLowerBoundArray.GetUpperBound(0); i++)
{
    customLowerBoundArray.SetValue(value, i);
    value *= 5;
}

for (var i = customLowerBoundArray.GetLowerBound(0); i <= customLowerBoundArray.GetUpperBound(0); i++)
{
    Console.WriteLine(customLowerBoundArray.GetValue(i));
}

We make use of Array.GetLowerBound() and Array.GetUpperBound() methods to determine the start and end index of the custom lower bound array:

2
10
50
250
1250

List – Incorrect Arguments

This scenario is a little bit different:

Console.WriteLine("Please enter a search argument: ");

var searchArg = Convert.ToInt32(Console.ReadLine());
var multiplesOfFive = new List<int> { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 };

Console.WriteLine($"Let's display the multiples of 5 greater than {searchArg}");

var startIndex = multiplesOfFive.IndexOf(searchArg);

for (var i = startIndex; i < multiplesOfFive.Count; i++)
{
    Console.WriteLine(multiplesOfFive[i]);
}

We have a List<int> to which we add the first 10 multiples of 5.

We take input from the user – searchArg, and use this input to display numbers greater than the input.

An input other than the numbers present in the list results in an index of -1, which raises the ArgumentOutOfRangeException.

An input of 12 breaks the code and we get the exception:

Please enter a search argument:
12
Let's display the even numbers greater than 12
Unhandled exception. 
System.ArgumentOutOfRangeException: 
Index was out of range. Must be non-negative and less than the size of the collection.

Solution

To prevent the exception, we can add a check:

Console.WriteLine("Please enter a search argument!!!");

var searchArg = Convert.ToInt32(Console.ReadLine());
var multiplesOfFive = new List<int> { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 };

var startIndex = multiplesOfFive.IndexOf(searchArg);
if (startIndex < 0)
{
    Console.WriteLine($"The number {searchArg} does not exist in the list.");
}
else
{
    Console.WriteLine($"Let's display the even numbers greater than {searchArg}");
    for (var i = startIndex; i < multiplesOfFive.Count; i++)
    {
        Console.WriteLine(multiplesOfFive[i]);
    }
}

An incorrect input (other than the numbers in the list) results in a user-friendly message:

Please enter a search argument:
22
The number 22 does not exist in the list.

IndexOutOfRangeException vs ArgumentOutOfRangeException

Lists raise ArgumentOutOfRangeException when an item is accessed at an invalid index, whereas arrays raise IndexOutOfRangeException for the same behavior:

IList<string> tempList = new string[] { "0" };
var foo = tempList[-1]; //ArgumentOutOfRangeException

string[] tempArray = new string[] { "0" };
var bar = tempArray[-1]; //IndexOutOfRangeException

Two Arrays – Different Lengths

The IndexOutOfRangeException is raised when we attempt to assign an array element to a different array that has fewer elements than the first array:

var array1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var array2 = new int[7];

array2[array1.Length - 1] = array1[array1.Length - 1];

The array1 length is 10, while the array2 length is 7.

We are trying to assign the last element of array1 to array2, but since array2 is smaller (length is 7), we get the IndexOutOfRangeException.

Solution

Now, let’s see how we can prevent the exception:

var array1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var array2 = new int[array1.Length];

array2[array1.Length - 1] = array1[array1.Length - 1];
Console.WriteLine($"The last element of array2 is: {array2[array2.Length - 1]}");

We initialize the second array as similar to the first array’s length:

The last element of array2 is: 10

Getting Confused Between an Index and the Value of That Index

Let’s see this case with an example:

var oddNumbersArray = new int[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
foreach (var number in oddNumbersArray)
{
    Console.WriteLine($"The odd number is: {oddNumbersArray[number]}");
}

We have an array of odd numbers, we run a foreach loop on the array and try to access the element using the index and not the element itself.

We start encountering  IndexOutOfRangeException when the number == 11 acts as the index.

There is no element at the 11th index:

The odd number is: 3
The odd number is: 7
The odd number is: 11
The odd number is: 15
The odd number is: 19
Unhandled exception. 
System.IndexOutOfRangeException: Index was outside the bounds of the array.

Solution

We can prevent the exception easily by using the value itself, instead of trying to access the value by index in each iteration:

var oddNumbersArray = new int[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
foreach (var number in oddNumbersArray)
{
    Console.WriteLine($"The odd number is: {number}");
}

This effectively prevents the exception:

The odd number is: 1
The odd number is: 3
The odd number is: 5
The odd number is: 7
The odd number is: 9
The odd number is: 11
The odd number is: 13
The odd number is: 15
The odd number is: 17
The odd number is: 19

DataTable – Incorrect Column Index

We create a method to render a static DataTable in a separate StaticDataRepository class:

public class StaticDataRepository
{
    public static DataTable GetGroceries()
    {
        var dataTable = new DataTable
        {
            TableName = "Groceries"
        };
        dataTable.Columns.Add("Id", typeof(int));
        dataTable.Columns.Add("Name", typeof(string));
        dataTable.Columns.Add("Description", typeof(string));

        dataTable.Rows.Add(1, "Macaroni", "Tasty Pasta");
        dataTable.Rows.Add(2, "Ramen", "Tasty Noodles");
        dataTable.Rows.Add(3, "Figaro Oil", "Olive Oil");
        dataTable.Rows.Add(4, "XYZ Lentils", "Nutritious Pulses");

        return dataTable;
    }
}

We then run a foreach loop on the data table and try to access all the rows:

var groceries = StaticDataRepository.GetGroceries();
for (var i = 0; i < groceries.Rows.Count; i++)
{
    var row = groceries.Rows[i];
    Console.WriteLine($"{row[0]}t {row[1]}t {row[2]}t {row[3]}");
}

The code raises an IndexOutOfRangeException because we are trying to access the 4th column (row[3]), which is not present in the data table:

Unhandled exception. 
System.IndexOutOfRangeException: Cannot find column 3.

It is quite possible that we might not be aware of the number of columns in the incoming data table.

We may try to access a column that does not exist and encounter the exception.

Solution

Let’s see how we can prevent the exception in this scenario:

var groceries = StaticDataRepository.GetGroceries();
 
foreach (DataRow grocery in groceries.Rows)
{
    foreach (var item in grocery.ItemArray)
    {
        Console.WriteLine($"{item}");
    }
 
    Console.WriteLine("********************");
}

We do not hardcode the indexes, rather we make use of the inbuilt properties such as ItemArray that is an array of objects:

1
Macaroni
Tasty Pasta
********************
2
Ramen
Tasty Noodles
********************
3
Figaro Oil
Olive Oil
********************
4
XYZ Lentils
Nutritious Pulses
********************

These were the most common situations where we can stumble upon the IndexOutOfRangeException.

Conclusion

We have learned about IndexOutOfRangeException in detail, the various scenarios where the exception gets raised, and the possible solutions.

что это?

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

Когда Его Бросают

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

 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;
    }
}
 

This code will then fail because array is 0-based and last (bottom-right) pixel in the image is pixels[imageWidth - 1, imageHeight - 1] :

 pixels[imageWidth, imageHeight] = Color.Black;
 

In another scenario you may get ArgumentOutOfRangeException for this code (for example if you’re using GetPixel method on a Bitmap class).

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")]);
 

This will fail if no items in myArray will satisfy search condition because Array.IndexOf() will return -1 and then array access will throw.

Next example is a naive example to calculate occurrences of a given set of numbers (knowing maximum number and returning an array where item at index 0 represents number 0, items at index 1 represents number 1 and so on):

 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> запускается пустым — поэтому попытка доступа к элементам только что созданного списка приводит к этому исключению.

 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 }));
}
 

Validate Parameters
If index comes from a parameter you should always validate them (throwing appropriate ArgumentException or ArgumentOutOfRangeException ). In the next example, wrong parameters may cause IndexOutOfRangeException , users of this function may expect this because they’re passing an array but it’s not always so obvious. I’d suggest to always validate parameters for public functions:

 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;
 

You found your error, check how index increases. Is it right? Check how array is allocated, is coherent with how index increases? Is it right according to your specifications? If you answer yes to all these questions, then you’ll find good help here on StackOverflow but please first check for that by yourself. You’ll save your own time!

A good start point is to always use assertions and to validate inputs. You may even want to use code contracts. When something went wrong and you can’t figure out what happens with a quick look at your code then you have to resort to an old friend: debugger. Just run your application in debug inside Visual Studio (or your favorite IDE), you’ll see exactly which line throws this exception, which array is involved and which index you’re trying to use. Really, 99% of the times you’ll solve it by yourself in a few minutes.

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

В 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 правило

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

What Is It?

This exception means that you’re trying to access a collection item by index, using an invalid index. An index is invalid when it’s lower than the collection’s lower bound or greater than or equal to the number of elements it contains.

When It Is Thrown

Given an array declared as:

byte[] array = new byte[4];

You can access this array from 0 to 3, values outside this range will cause IndexOutOfRangeException to be thrown. Remember this when you create and access an array.

Array Length
In C#, usually, arrays are 0-based. It means that first element has index 0 and last element has index Length - 1 (where Length is total number of items in the array) so this code doesn’t work:

array[array.Length] = 0;

Moreover please note that if you have a multidimensional array then you can’t use Array.Length for both dimension, you have to use 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;
    }
}

Upper Bound Is Not Inclusive
In the following example we create a raw bidimensional array of Color. Each item represents a pixel, indices are from (0, 0) to (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;
    }
}

This code will then fail because array is 0-based and last (bottom-right) pixel in the image is pixels[imageWidth - 1, imageHeight - 1]:

pixels[imageWidth, imageHeight] = Color.Black;

In another scenario you may get ArgumentOutOfRangeException for this code (for example if you’re using GetPixel method on a Bitmap class).

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")]);

This will fail if no items in myArray will satisfy search condition because Array.IndexOf() will return -1 and then array access will throw.

Next example is a naive example to calculate occurrences of a given set of numbers (knowing maximum number and returning an array where item at index 0 represents number 0, items at index 1 represents number 1 and so on):

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;
}

Of course, it’s a pretty terrible implementation but what I want to show is that it’ll fail for negative numbers and numbers above maximum.

How it applies to List<T>?

Same cases as array — range of valid indexes — 0 (List‘s indexes always start with 0) to list.Count — accessing elements outside of this range will cause the exception.

Note that List<T> throws ArgumentOutOfRangeException for the same cases where arrays use IndexOutOfRangeException.

Unlike arrays, List<T> starts empty — so trying to access items of just created list lead to this exception.

var list = new List<int>();

Common case is to populate list with indexing (similar to Dictionary<int, T>) will cause exception:

list[0] = 42; // exception
list.Add(42); // correct

IDataReader and Columns
Imagine you’re trying to read data from a database with this code:

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() will throw IndexOutOfRangeException because you’re dataset has only two columns but you’re trying to get a value from 3rd one (indices are always 0-based).

Please note that this behavior is shared with most IDataReader implementations (SqlDataReader, OleDbDataReader and so on).

You can get the same exception also if you use the IDataReader overload of the indexer operator that takes a column name and pass an invalid column name.
Suppose for example that you have retrieved a column named Column1 but then you try to retrieve the value of that field with

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

This happens because the indexer operator is implemented trying to retrieve the index of a Colum1 field that doesn’t exist. The GetOrdinal method will throw this exception when its internal helper code returns a -1 as the index of «Colum1».

Others
There is another (documented) case when this exception is thrown: if, in DataView, data column name being supplied to the DataViewSort property is not valid.

How to Avoid

In this example, let me assume, for simplicity, that arrays are always monodimensional and 0-based. If you want to be strict (or you’re developing a library), you may need to replace 0 with GetLowerBound(0) and .Length with GetUpperBound(0) (of course if you have parameters of type System.Array, it doesn’t apply for T[]). Please note that in this case, upper bound is inclusive then this code:

for (int i=0; i < array.Length; ++i) { }

Should be rewritten like this:

for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) { }

Please note that this is not allowed (it’ll throw InvalidCastException), that’s why if your parameters are T[] you’re safe about custom lower bound arrays:

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 }));
}

Validate Parameters
If index comes from a parameter you should always validate them (throwing appropriate ArgumentException or ArgumentOutOfRangeException). In the next example, wrong parameters may cause IndexOutOfRangeException, users of this function may expect this because they’re passing an array but it’s not always so obvious. I’d suggest to always validate parameters for public functions:

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 && 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;

You found your error, check how index increases. Is it right? Check how array is allocated, is coherent with how index increases? Is it right according to your specifications? If you answer yes to all these questions, then you’ll find good help here on StackOverflow but please first check for that by yourself. You’ll save your own time!

A good start point is to always use assertions and to validate inputs. You may even want to use code contracts. When something went wrong and you can’t figure out what happens with a quick look at your code then you have to resort to an old friend: debugger. Just run your application in debug inside Visual Studio (or your favorite IDE), you’ll see exactly which line throws this exception, which array is involved and which index you’re trying to use. Really, 99% of the times you’ll solve it by yourself in a few minutes.

If this happens in production then you’d better to add assertions in incriminated code, probably we won’t see in your code what you can’t see by yourself (but you can always bet).

The VB.NET side of the story

Everything that we have said in the C# answer is valid for VB.NET with the obvious syntax differences but there is an important point to consider when you deal with VB.NET arrays.

In VB.NET, arrays are declared setting the maximum valid index value for the array. It is not the count of the elements that we want to store in the array.

' declares an array with space for 5 integer 
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer

So this loop will fill the array with 5 integers without causing any IndexOutOfRangeException

For i As Integer = 0 To 4
    myArray(i) = i
Next

The VB.NET rule

This exception means that you’re trying to access a collection item by index, using an invalid index. An index is invalid when it’s lower than the collection’s lower bound or greater than equal to the number of elements it contains. the maximum allowed index defined in the array declaration

Понравилась статья? Поделить с друзьями:
  • Как исправить информацию в яндексе в моей компании
  • Как найти направляющие векторы сторон
  • Как найти нтв канал онлайн
  • Как найти первую работу python
  • Как найти ширину тротуара