什么是 IndexOutOfRangeException / ArgumentOutOfRangeException 以及如何修复它?

2023-12-22

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

指数数组的边界之外。

这是什么意思?我能做什么?

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

mscorlib.dll 中发生“System.ArgumentOutOfRangeException”类型的异常,但未在用户代码中处理其他信息:索引超出范围。必须为非负数且小于集合的大小。


它是什么?

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

当它被抛出时

给定一个声明为的数组:

byte[] array = new byte[4];

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

数组长度
在 C# 中,数组通常是从 0 开始的。这意味着第一个元素的索引为 0,最后一个元素的索引为Length - 1 (where 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) 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]:

pixels[imageWidth, imageHeight] = Color.Black;

在另一种情况下你可能会得到ArgumentOutOfRangeException对于此代码(例如,如果您使用GetPixel上的方法Bitmap class).

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

Array.Resize(ref array, array.Length + 1);

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

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

var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });

在该示例中,数组索引的有效范围是 1 到 4。当然,上限不能更改。

错误的论据
如果您使用未经验证的参数(来自用户输入或函数用户)访问数组,您可能会收到此错误:

private static string[] RomanNumbers =
    new string[] { "I", "II", "III", "IV", "V" };

public static string Romanize(int number)
{
    return RomanNumbers[number];
}

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

// 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,然后数组访问将抛出异常。

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

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.

它如何适用于List<T> http://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx?

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

注意List<T> throws 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因为你的数据集只有两列,但你试图从第三列获取一个值(索引是always从 0 开始)。

请注意,大多数人都有这种行为IDataReader实施(SqlDataReader, OleDbDataReader等等)。

如果您使用索引器运算符的 IDataReader 重载(该运算符采用列名并传递无效的列名),您也会遇到相同的异常。
例如,假设您检索了名为Column1但随后您尝试使用以下命令检索该字段的值

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

发生这种情况是因为索引器运算符被实现为尝试检索某个对象的索引Colum1不存在的字段。当 GetOrdinal 方法的内部帮助程序代码返回 -1 作为“Colum1”的索引时,该方法将引发此异常。

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

如何避免

在此示例中,为简单起见,让我假设数组始终是单维且从 0 开始的。如果你想严格一些(或者你正在开发一个库),你可能需要替换0 with GetLowerBound(0) and .Length with GetUpperBound(0)(当然,如果你有类型的参数System.Array,它不适用于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 }));
}

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

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逻辑与Debug.Assert():

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

检查对象状态
数组索引可能不直接来自参数。它可能是对象状态的一部分。一般来说,验证对象状态始终是一个好习惯(如果需要,可以单独验证对象状态,也可以使用函数参数)。您可以使用Debug.Assert(),抛出一个适当的异常(更多地描述问题)或像本例中那样处理:

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

int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }

如何调试

在我看来,关于这个错误的大多数问题都可以简单地避免。您花在编写正确问题(带有一个小的工作示例和一个小的解释)上的时间很容易比调试代码所需的时间多得多。首先,请阅读 Eric Lippert 的博客文章:小程序的调试 http://ericlippert.com/2014/03/05/how-to-debug-small-programs/,我不会在这里重复他的话,但这绝对是一个必读.

您有源代码,您有带有堆栈跟踪的异常消息。去那里,选择正确的行号,你会看到:

array[index] = newValue;

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

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

如果这种情况发生在生产中,那么您最好在受控代码中添加断言,可能我们不会在您的代码中看到您自己看不到的东西(但您总是可以打赌)。

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 个整数填充数组,而不会导致任何索引超出范围异常

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

VB.NET 规则

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是 IndexOutOfRangeException / ArgumentOutOfRangeException 以及如何修复它? 的相关文章

  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • VB.NET 中的静态方法实现

    我很困惑Static在 VB NET 中的实现 在 C 中 我们可以创建静态类和静态方法来为我们的应用程序编写实用方法 现在 VB NET 让我们创建Module代替静态类 如果我们在模块中创建一个方法 默认情况下它会变成静态的 但在我的应
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 引用的程序集自动由 Visual Studio 替换

    我有 2 个项目 一个可移植类库和一个常规单元测试项目 在可移植类库中 我使用 NuGet 来引用 Microsoft BCL 可移植包 它附带 2 个程序集 System Threading Tasks dll and System Ru
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • C# 使用“?” if else 语句设置值这叫什么

    嘿 我刚刚看到以下声明 return name null name NA 我只是想知道这在 NET 中叫什么 是吗 代表即然后执行此操作 这是一个俗称的 条件运算符 三元运算符 http en wikipedia org wiki Tern
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐