In this article, we are going to learn about one of the most common exceptions in C# – IndexOutOfRangeException.
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.
What is IndexOutOfRangeException?
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.
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.