什么是 IndexOutOfRangeException/ArgumentOutOfRangeException 以及如何修复它? [英] What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?

查看:64
本文介绍了什么是 IndexOutOfRangeException/ArgumentOutOfRangeException 以及如何修复它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码,当它执行时,它抛出一个IndexOutOfRangeException,说,

I have some code and when it executes, it throws a IndexOutOfRangeException, saying,

索引超出数组范围.

这是什么意思,我该怎么办?

What does this mean, and what can I do about it?

根据使用的类,它也可以是 ArgumentOutOfRangeException

Depending on classes used it can also be ArgumentOutOfRangeException

mscorlib.dll 中出现类型为System.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.

推荐答案

这是什么?

此异常意味着您尝试使用无效索引按索引访问集合项.当索引低于集合的下限或大于或等于其包含的元素数时,索引无效.

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.

给定一个数组声明为:

byte[] array = new byte[4];

您可以从 0 到 3 访问此数组,超出此范围的值将导致抛出 IndexOutOfRangeException.创建和访问数组时请记住这一点.

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.

数组长度
在 C# 中,通常数组是从 0 开始的.这意味着第一个元素的索引为 0,最后一个元素的索引为 Length - 1(其中 Length 是数组中的项目总数),因此此代码不起作用:

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;

此外请注意,如果您有一个多维数组,则不能对两个维度都使用 Array.Length,您必须使用 Array.GetLength():

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 的原始二维数组.每一项代表一个像素,索引从(0, 0)(imageWidth - 1, imageHeight - 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;
    }
}

此代码将失败,因为数组是从 0 开始的,并且图像中的最后一个(右下角)像素是 pixels[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]:

pixels[imageWidth, imageHeight] = Color.Black;

在另一种情况下,您可能会为此代码获得 ArgumentOutOfRangeException(例如,如果您在 Bitmap 类上使用 GetPixel 方法).

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

数组不会增长
数组很快.与其他所有集合相比,线性搜索速度非常快.这是因为项目在内存中是连续的,所以可以计算内存地址(增量只是一个加法).无需遵循节点列表,简单的数学运算!您为此付出了一个限制:它们不能增长,如果您需要更多元素,您需要重新分配该数组(如果必须将旧项目复制到新块,这可能需要相对较长的时间).您可以使用 Array.Resize() 调整它们的大小,此示例向现有数组添加一个新条目:

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

不要忘记有效索引是从 0Length - 1.如果您只是尝试在 Length 处分配一个项目,您将得到 IndexOutOfRangeException(如果您认为它们可能会使用类似于 Insert 的语法增加,这种行为可能会让您感到困惑) 其他集合的方法).

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).

特殊具有自定义下界的数组
数组中的第一项总是索引为 0.这并不总是正确的,因为您可以创建一个具有自定义下限的数组:

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

在那个例子中,数组索引从 1 到 4 有效.当然,上限不能改变.

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

意外结果
抛出此异常的另一个原因也可能是:按照惯例,许多搜索函数将返回 -1(可空值已在 .NET 2.0 中引入,无论如何它也是多年来使用的众所周知的约定) 如果他们没有找到任何东西.假设您有一个与字符串相当的对象数组.你可能会想写这样的代码:

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

如果 myArray 中没有任何项目满足搜索条件,这将失败,因为 Array.IndexOf() 将返回 -1,然后数组访问将抛出.

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

下一个示例是计算给定数字集出现次数的简单示例(知道最大数字并返回一个数组,其中索引 0 处的项目表示数字 0,索引 1 处的项目表示数字 1,依此类推):

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

当然,这是一个非常糟糕的实现,但我想表明的是,对于负数和高于 maximum 的数字,它会失败.

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.

它如何适用于 List?

与数组相同的情况 - 有效索引范围 - 0(List 的索引总是从 0 开始)到 list.Count - 访问此范围之外的元素将导致异常.

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.

请注意,对于数组使用 IndexOutOfRangeException 的相同情况,List 会抛出 ArgumentOutOfRangeException.

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

与数组不同,List 以空开头 - 因此尝试访问刚创建的列表的项目会导致此异常.

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

var list = new List<int>();

常见的情况是用索引填充列表(类似于Dictionary)会导致异常:

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 和列
想象一下,您正在尝试使用以下代码从数据库读取数据:

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() 将抛出 IndexOutOfRangeException 因为您的数据集只有两列,但您正试图从第三列中获取值(索引是 总是基于 0).

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).

请注意,此行为与大多数 IDataReader 实现(SqlDataReaderOleDbDataReader 等)共享.

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

如果您使用索引器运算符的 IDataReader 重载接受列名并传递无效的列名,您也会得到相同的异常.
例如,假设您检索了一个名为 Column1 的列,但随后您尝试使用

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.

发生这种情况是因为实现了索引器运算符,试图检索不存在的 Colum1 字段的索引.GetOrdinal 方法在其内部帮助程序代码返回 -1 作为Colum1"的索引时将抛出此异常.

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".

其他
抛出此异常时还有另一种(已记录的)情况:如果在 DataView 中,提供给 DataViewSort 属性的数据列名称无效.

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.

在这个例子中,为了简单起见,让我假设数组总是一维的并且从 0 开始.如果你想要严格(或者你正在开发一个库),你可能需要用 GetLowerBound(0).Length 替换 0> 与 GetUpperBound(0) (当然如果你有 System.Array 类型的参数,它不适用于 T[]).请注意,在这种情况下,上限包含在此代码中:

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

应该改写成这样:

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

请注意,这是不允许的(它会抛出 InvalidCastException),这就是为什么如果你的参数是 T[] 你对自定义下限数组是安全的:

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

验证参数
如果索引来自参数,您应该始终验证它们(抛出适当的 ArgumentExceptionArgumentOutOfRangeException).在下一个示例中,错误的参数可能会导致 IndexOutOfRangeException,此函数的用户可能会期望这一点,因为他们正在传递一个数组,但并不总是那么明显.我建议始终验证公共函数的参数:

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

如果函数是私有的,你可以简单地用 Debug.Assert() 替换 if 逻辑:

If function is private you may simply replace if logic with Debug.Assert():

Debug.Assert(from >= 0 && from < array.Length);

检查对象状态
数组索引可能不直接来自参数.它可能是对象状态的一部分.一般来说,验证对象状态(如果需要,通过自身和函数参数)始终是一个很好的做法.您可以使用 Debug.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:

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() 返回值.如果我们知道它可能会失败,那么最好处理这种情况:

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

如何调试

在我看来,关于此错误的大多数问题都可以简单地避免.您花在编写正确问题上的时间(带有一个小的工作示例和一个小的解释)很容易比调试代码所需的时间多得多.首先,阅读 Eric Lippert 的关于 调试小程序,我不会在这里重复他的话,但绝对是必读.

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;

您发现了错误,请检查 index 如何增加.这样对吗?检查数组是如何分配的,与index如何增加一致?是否符合您的规格?如果您对所有这些问题的回答都是,那么您会在 StackOverflow 上找到很好的帮助,但请先自行检查.您将节省自己的时间!

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!

一个好的起点是始终使用断言并验证输入.您甚至可能想要使用代码契约.如果出现问题并且您无法通过快速查看代码来弄清楚发生了什么,那么您必须求助于老朋友:调试器.只需在 Visual Studio(或您最喜欢的 IDE)中的调试中运行您的应用程序,您就会准确地看到哪一行引发了此异常、涉及哪个数组以及您尝试使用哪个索引.真的,99% 的情况下,您会在几分钟内自行解决.

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).

我们在 C# 回答中所说的一切都对 VB.NET 有效,但存在明显的语法差异,但在处理 VB.NET 数组时有一个重要的点需要考虑.

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.

在 VB.NET 中,数组被声明为设置数组的最大有效索引值.它不是我们想要存储在数组中的元素的数量.

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

所以这个循环将用 5 个整数填充数组,而不会导致任何 IndexOutOfRangeException

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

VB.NET 规则

此异常意味着您尝试使用无效索引按索引访问集合项.当索引低于集合的下限或大于 等于它包含的元素数时,索引无效. 数组声明中定义的最大允许索引

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

这篇关于什么是 IndexOutOfRangeException/ArgumentOutOfRangeException 以及如何修复它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆