About IndexOutOfRangeException :
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.
Imagine you're trying to read data from a database with this code:
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;
}
}
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 be
expansive 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];
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
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".
How to Avoid
In this examples let me assume, for simplicity, that arrays are always
monodimensional and 0-based. If you want to be strict (or your'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 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);
}
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 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 times you'll solve it by yourself in 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).
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 the 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
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

No comments:
Post a Comment