什么是 IndexOutOfRangeException / ArgumentOutOfRangeException 以及如何修复它?
我有一些代码,当它执行时,它会抛出一个 IndexOutOfRangeException ,说:
索引超出了数组的范围。
这是什么意思?我能做什么?
根据所使用的类,它也可以是ArgumentOutOfRangeException
mscorlib.dll 中发生“System.ArgumentOutOfRangeException”类型的异常,但未在用户代码中处理其他信息:索引超出范围。必须为非负数且小于集合的大小。
I have some code and when it executes, it throws a IndexOutOfRangeException
, saying,
Index was outside the bounds of the array.
What does this mean, and what can I do about it?
Depending on classes used it can also be ArgumentOutOfRangeException
An exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll but was not handled in user code Additional information: Index was out of range. Must be non-negative and less than the size of the collection.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
它是什么?
此异常意味着您尝试使用无效索引通过索引访问集合项。当索引低于集合的下限或大于或等于其包含的元素数时,索引无效。
何时抛出
给定一个声明为的数组:
您可以从 0 到 3 访问此数组,超出此范围的值将导致抛出 IndexOutOfRangeException。创建和访问数组时请记住这一点。
数组长度
在 C# 中,数组通常是从 0 开始的。这意味着第一个元素的索引为 0,最后一个元素的索引为
Length - 1
(其中Length
是数组中的项目总数),因此此代码不起作用:此外请注意,如果您有一个多维数组,则不能对两个维度都使用 Array.Length,您必须使用 Array.GetLength():
不包含上限
在下面的示例中,我们创建一个
Color
的原始二维数组。每一项代表一个像素,索引从(0, 0)
到(imageWidth - 1, imageHeight - 1)
。然后,此代码将失败,因为数组是从 0 开始的,并且图像中的最后一个(右下角)像素是
pixels[imageWidth - 1, imageHeight - 1]
:在另一种情况下,您可能会得到
此代码的 ArgumentOutOfRangeException
(例如,如果您在Bitmap
类上使用GetPixel
方法)。数组不会增长() 调整它们的大小,此示例向现有数组添加一个新条目:
数组速度很快。与其他所有集合相比,线性搜索速度非常快。这是因为项目在内存中是连续的,因此可以计算内存地址(增量只是加法)。无需遵循节点列表,简单的数学运算!您付出的代价是有限制的:它们不能增长,如果您需要更多元素,您需要重新分配该数组(如果必须将旧项目复制到新块,这可能需要相对较长的时间)。您可以使用 Array.Resize
不要忘记有效索引是从
0
到 <代码>长度 - 1。如果您只是尝试在Length
处分配一个项目,您将得到IndexOutOfRangeException
(如果您认为它们可能会使用类似于Insert的语法增加,那么这种行为可能会让您感到困惑
其他集合的方法)。具有自定义下界的特殊数组
数组中的第一项始终索引为 0。这并不总是正确的,因为您可以创建具有自定义下限的数组:
在该示例中,数组索引的有效范围是 1 到 4。当然,上限不能更改。
错误的论点
如果您使用未经验证的参数(来自用户输入或函数用户)访问数组,您可能会收到此错误:
意外结果
抛出此异常也可能是由于另一个原因:按照惯例,许多搜索函数将返回 -1(nullables 已随 .NET 2.0 引入,无论如何,它也是多年来使用的众所周知的约定)如果他们没有找到任何东西。假设您有一个与字符串相当的对象数组。您可能会考虑编写以下代码:
如果
myArray
中没有项目满足搜索条件,则会失败,因为Array.IndexOf()
将返回 -1,然后数组访问将抛出异常。下一个示例是一个简单的示例,用于计算给定数字集的出现次数(知道最大数字并返回一个数组,其中索引 0 处的项目代表数字 0,索引 1 处的项目代表数字 1 等等):
当然,这是一个漂亮的例子糟糕的实现,但我想展示的是,对于负数和高于
最大值
的数字,它会失败。如何应用于
列表< ;T>
?与数组相同 - 有效索引范围 - 0(
List
的索引始终以 0 开头)到list.Count
- 访问此范围之外的元素将导致异常。请注意,对于数组使用
IndexOutOfRangeException
的相同情况,List
会抛出ArgumentOutOfRangeException
。与数组不同,
List
开头为空 - 因此尝试访问刚刚创建的列表中的项目会导致此异常。常见情况是使用索引填充列表(类似于
Dictionary
)会导致异常:IDataReader 和 Columns
想象一下,您尝试使用以下代码从数据库读取数据:
GetString()
将抛出IndexOutOfRangeException
因为您的数据集只有两列,但您试图从第三个获取一个值(索引始终从0开始)。请注意,大多数
IDataReader
实现(SqlDataReader
、OleDbDataReader
等)都具有此行为。如果您使用索引器运算符的 IDataReader 重载(该运算符采用列名并传递无效的列名),您也会遇到相同的异常。
例如,假设您检索了名为 Column1 的列,但随后尝试检索该字段的值,则会
发生这种情况,因为索引器运算符是在尝试检索 Colum1< 的索引时实现的。 /em> 不存在的字段。当 GetOrdinal 方法的内部帮助程序代码返回 -1 作为“Colum1”的索引时,该方法将引发此异常。
其他
抛出此异常时还有另一种(已记录)情况:如果在
DataView
中,提供给DataViewSort
属性的数据列名称无效。如何避免
在本例中,为简单起见,让我假设数组始终是单维且从 0 开始的。如果您想要严格(或者您正在开发库),则可能需要将
0
替换为GetLowerBound(0)
和.Length
> 与GetUpperBound(0)
(当然,如果您有System.Arra
y 类型的参数,则它不适用于T[]
)。请注意,在这种情况下,上限是包含在内的,那么此代码:应该像这样重写:
请注意,这是不允许的(它将抛出
InvalidCastException
),这就是为什么如果您的参数是 < code>T[] 您对自定义下界数组是安全的:验证参数
如果索引来自参数,您应该始终验证它们(抛出适当的
ArgumentException
或ArgumentOutOfRangeException
)。在下一个示例中,错误的参数可能会导致IndexOutOfRangeException
,此函数的用户可能会期望这种情况,因为他们传递的是数组,但并不总是那么明显。我建议始终验证公共函数的参数:如果函数是私有的,您可以简单地将
if
逻辑替换为Debug.Assert()
:检查对象状态< /strong>
数组索引可能不直接来自参数。它可能是对象状态的一部分。一般来说,验证对象状态始终是一个好习惯(如果需要,可以单独验证对象状态,也可以使用函数参数)。您可以使用 Debug.Assert(),抛出适当的异常(对问题更具描述性)或像此示例中那样处理:
验证返回值
在前面的示例之一中,我们直接使用了 Array.IndexOf() 返回值。如果我们知道它可能会失败,那么最好处理这种情况:
如何调试
在我看来,大多数关于此错误的问题都可以简单地避免。您花在编写正确问题(带有一个小工作示例和一个小解释)上的时间很容易比调试代码所需的时间多得多。首先,阅读 Eric Lippert 的关于调试小型程序的博客文章程序,我不会在这里重复他的话,但这绝对是必读。
您有源代码,您有带有堆栈跟踪的异常消息。转到那里,选择正确的行号,您会看到:
您发现错误,检查
index
如何增加。对吗?检查数组是如何分配的,与index
如何增加一致?是否符合您的规格?如果您对所有这些问题的回答都是是,那么您会在 StackOverflow 上找到很好的帮助,但请先自行检查。您将节省自己的时间!一个好的起点是始终使用断言并验证输入。您甚至可能想使用代码契约。当出现问题并且您无法通过快速查看代码来弄清楚发生了什么时,您必须求助于老朋友:调试器。只需在 Visual Studio(或您最喜欢的 IDE)内调试运行您的应用程序,您将准确地看到哪一行抛出此异常、涉及哪个数组以及您尝试使用哪个索引。确实,99% 的情况下你都会在几分钟内自己解决这个问题。
如果这种情况发生在生产中,那么您最好在受控代码中添加断言,可能我们不会在您的代码中看到您自己看不到的东西(但您总是可以打赌)。
故事的 VB.NET 方面
我们在 C# 答案中所说的所有内容都适用于 VB.NET,但有明显的语法差异,但在处理 VB.NET 数组时有一个需要考虑的重要点。
在 VB.NET 中,声明数组时设置数组的最大有效索引值。它不是我们要存储在数组中的元素的数量。
因此,此循环将用 5 个整数填充数组,而不会导致任何 IndexOutOfRangeException
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:
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
(whereLength
is total number of items in the array) so this code doesn't work:Moreover please note that if you have a multidimensional array then you can't use
Array.Length
for both dimension, you have to useArray.GetLength()
: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)
.This code will then fail because array is 0-based and last (bottom-right) pixel in the image is
pixels[imageWidth - 1, imageHeight - 1]
:In another scenario you may get
ArgumentOutOfRangeException
for this code (for example if you're usingGetPixel
method on aBitmap
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:Don't forget that valid indices are from
0
toLength - 1
. If you simply try to assign an item atLength
you'll getIndexOutOfRangeException
(this behavior may confuse you if you think they may increase with a syntax similar toInsert
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:
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:
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:
This will fail if no items in
myArray
will satisfy search condition becauseArray.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):
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) tolist.Count
- accessing elements outside of this range will cause the exception.Note that
List<T>
throwsArgumentOutOfRangeException
for the same cases where arrays useIndexOutOfRangeException
.Unlike arrays,
List<T>
starts empty - so trying to access items of just created list lead to this exception.Common case is to populate list with indexing (similar to
Dictionary<int, T>
) will cause exception:IDataReader and Columns
Imagine you're trying to read data from a database with this code:
GetString()
will throwIndexOutOfRangeException
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
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 theDataViewSort
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
withGetLowerBound(0)
and.Length
withGetUpperBound(0)
(of course if you have parameters of typeSystem.Arra
y, it doesn't apply forT[]
). Please note that in this case, upper bound is inclusive then this code:Should be rewritten like this:
Please note that this is not allowed (it'll throw
InvalidCastException
), that's why if your parameters areT[]
you're safe about custom lower bound arrays:Validate Parameters
If index comes from a parameter you should always validate them (throwing appropriate
ArgumentException
orArgumentOutOfRangeException
). In the next example, wrong parameters may causeIndexOutOfRangeException
, 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:If function is private you may simply replace
if
logic withDebug.Assert()
: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: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: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:
You found your error, check how
index
increases. Is it right? Check how array is allocated, is coherent with howindex
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.
So this loop will fill the array with 5 integers without causing any IndexOutOfRangeException
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关于什么是索引越界异常的简单解释:
假设有一列火车在那里,它的车厢是 D1、D2、D3。
一名乘客上车,他持有D4的车票。
现在会发生什么。乘客想要进入一个不存在的车厢,显然会出现问题。
同样的情况:每当我们尝试访问数组列表等时,我们只能访问数组中现有的索引。
array[0]
和array[1]
已存在。如果我们尝试访问array[3]
,它实际上并不存在,因此会出现索引越界异常。Simple explanation about what a Index out of bound exception is:
Just think one train is there its compartments are D1,D2,D3.
One passenger came to enter the train and he have the ticket for D4.
now what will happen. the passenger want to enter a compartment that does not exist so obviously problem will arise.
Same scenario: whenever we try to access an array list, etc. we can only access the existing indexes in the array.
array[0]
andarray[1]
are existing. If we try to accessarray[3]
, it's not there actually, so an index out of bound exception will arise.为了容易理解这个问题,假设我们编写了这段代码:
结果将是:
数组的大小为 3(索引 0、1 和 2),但 for 循环循环 4 次(0、1、2 和 3)。
因此,当它尝试使用 (3) 访问边界之外时,它会抛出异常。
To easily understand the problem, imagine we wrote this code:
Result will be:
Size of array is 3 (indices 0, 1 and 2), but the for-loop loops 4 times (0, 1, 2 and 3).
So when it tries to access outside the bounds with (3) it throws the exception.
与许多其他异常类型相比,从非常长的完整接受答案的角度来看,有一个重要的点需要说明
IndexOutOfRangeException
,那就是:通常存在可能难以控制的复杂程序状态代码中的特定点,例如数据库连接断开,因此无法检索输入的数据等...此类问题通常会导致某种异常,该异常必须上升到更高级别,因为它发生的位置没有那时的处理方式。
IndexOutOfRangeException 通常有所不同,因为在大多数情况下,在引发异常的位置进行检查非常简单。一般来说,这种异常是由一些代码抛出的,这些代码可以很容易地在问题发生的地方处理问题——只需检查数组的实际长度。您不想通过更高层处理此异常来“修复”此问题 - 而是通过确保它不会在第一个实例中抛出 - 在大多数情况下,通过检查数组长度很容易做到。
另一种说法是,由于真正缺乏对输入或程序状态的控制,可能会出现其他异常,但 IndexOutOfRangeException 通常只是飞行员(程序员)错误。
A side from the very long complete accepted answer there is an important point to make about
IndexOutOfRangeException
compared with many other exception types, and that is:Often there is complex program state that maybe difficult to have control over at a particular point in code e.g a DB connection goes down so data for an input cannot be retrieved etc... This kind of issue often results in an Exception of some kind that has to bubble up to a higher level because where it occurs has no way of dealing with it at that point.
IndexOutOfRangeException
is generally different in that it in most cases it is pretty trivial to check for at the point where the exception is being raised. Generally this kind of exception get thrown by some code that could very easily deal with the issue at the place it is occurring - just by checking the actual length of the array. You don't want to 'fix' this by handling this exception higher up - but instead by ensuring its not thrown in the first instance - which in most cases is easy to do by checking the array length.Another way of putting this is that other exceptions can arise due to genuine lack of control over input or program state BUT
IndexOutOfRangeException
more often than not is simply just pilot (programmer) error.这两种异常在各种编程语言中都很常见,正如其他人所说,这是当您访问索引大于数组大小的元素时。例如:
这背后的主要原因是编译器通常不会检查这些东西,因此它们只会在运行时表达自己。
与此类似:
为什么不呢现代编译器会捕获对数组进行越界访问的尝试吗?
These two exceptions are common in various programming languages and as others said it's when you access an element with an index greater than the size of the array. For example:
The main reason behind this is compilers usually don't check this stuff, hence they will only express themselves at runtime.
Similar to this:
Why don't modern compilers catch attempts to make out-of-bounds access to arrays?
对于数组和 IEnumerable 等所有内容,我使用 ElementAt 和 ElementAtOrDefault LINQ 扩展方法来防止超出范围异常。但请考虑 ElementAtOrDefault 方法返回对象类型默认值,并且在可为 null 的类型中,如果数组中不存在给定的任何值的索引,则返回 null 作为结果。我的意思是,如果您尝试获取索引 -1 处的元素,ElementAtOrDefault 返回默认值,对于可为 null 的类型你会得到null。
示例:
并且...
for arrays and everything as IEnumerable i use ElementAt and ElementAtOrDefault LINQ extension method to prevent out of range exception. but consider ElementAtOrDefault method returns object type default value and in nullable types returns null as result if given index at any values does not exist in array.I mean if you ever try to get element at index -1 ElementAtOrDefault returns default value and for nullable types you will get null.
Example:
and...