Rus*_*nov 17 .net c# list indexoutofrangeexception
为什么List<T>.IndexOf允许超出范围的起始指数?
var list = new List<int>() { 100 };
Console.WriteLine(list.IndexOf(1/*item*/, 1/*start index*/));
Run Code Online (Sandbox Code Playgroud)
没有任何例外.但是1这个系列中没有带索引的项目!只有一个带0索引的项目.那么,为什么.Net允许你这样做呢?
Tam*_*red 12
首先,如果有人应该处理无效输入,那么它是运行时而不是编译器,因为输入具有相同的有效类型(int).
事实上,实际上,看到IndexOf使它成为一个实现错误的源代码:
[__DynamicallyInvokable]
public int IndexOf(T item, int index)
{
if (index > this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
}
return Array.IndexOf<T>(this._items, item, index, this._size - index);
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它的目的是不允许您插入一个大于列表大小的无效索引,但是比较是用>而不是>=.
以下代码返回0:
var list = new List<int>() { 100 };
Console.WriteLine(list.IndexOf(100/*item*/, 0/*start index*/));
Run Code Online (Sandbox Code Playgroud)以下代码返回-1:
var list = new List<int>() { 100 };
Console.WriteLine(list.IndexOf(100/*item*/, 1/*start index*/));
Run Code Online (Sandbox Code Playgroud)虽然下面的代码抛出Exception:
var list = new List<int>() { 100 };
Console.WriteLine(list.IndexOf(100/*item*/, 2/*start index*/));
Run Code Online (Sandbox Code Playgroud)对于第二种和第三种情况,没有理由表现出不同的行为,这使得它看起来像是执行中的一个错误IndexOf.
此外,文档说:
ArgumentOutOfRangeException | index超出了有效索引的范围
List<T>.
我们刚刚看到的并不是发生了什么.
注意:数组会发生相同的行为:
int[] arr = { 100 };
//Output: 0
Console.WriteLine(Array.IndexOf(arr, 100/*item*/, 0/*start index*/));
//Output: -1
Console.WriteLine(Array.IndexOf(arr, 100/*item*/, 1/*start index*/));
//Throws ArgumentOutOfRangeException
Console.WriteLine(Array.IndexOf(arr, 100/*item*/, 2/*start index*/));
Run Code Online (Sandbox Code Playgroud)
它允许它,因为有人认为这是好的,有人写了规范或实现了方法.
它也在List(T).IndexOf方法中有所记载:
0(零)在空列表中有效.
(我也认为这Count是任何列表的有效起始索引)
请注意,对于Array.IndexOfMethod,记录了相同的内容,但稍微更好地记录了:
如果
startIndex等于Array.Length,则该方法返回-1.如果startIndex大于Array.Length,则该方法抛出一个ArgumentOutOfRangeException.
让我在这里澄清我的答案.
你问"为什么这个方法允许这个输入".
唯一合法的原因是"因为有人实施了这个方法,所以它确实如此".
这是一个错误吗?它很可能是.文档只说0是空列表的合法起始索引,它没有直接说1对于包含单个元素的列表是合法的.该方法的异常文档似乎与此相矛盾(正如评论中提到的那样)似乎有利于它是一个错误.
但是"为什么会这样做"的唯一原因是有人实际上以这种方式实现了这种方法.它可能是一个有意识的选择,它可能是一个错误,它可能是代码或文档中的疏忽.
唯一可以判断它是哪一个的人将是实施此方法的人或人.
当然,唯一一个可以肯定地说出这个问题的人是做出这个决定的人.
我看到的唯一合乎逻辑的原因(这是我的猜测)是允许这样使用
for (int index = list.IndexOf(value); index >= 0; index = list.IndexOf(value, index + 1))
{
// do something
}
Run Code Online (Sandbox Code Playgroud)
或换句话说,能够从上次成功搜索的下一个索引安全地重新开始搜索.
这可能看起来不是很常见的情况,但是在处理字符串时是典型的模式(例如,当想要避免时Split).这提醒我String.IndexOf具有相同的行为并且有更好的文档记录(尽管没有指定原因):
startIndex参数的范围是0到字符串实例的长度.如果startIndex等于字符串实例的长度,则该方法返回-1.
要恢复,因为Array,string并且List<T>共享相同的行为,显然它的目的,绝对不是一个执行错误.
我想,我明白为什么。实现这样的方法比较容易。看:
public int IndexOf(T item, int index)
{
if (index > this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
}
return Array.IndexOf<T>(this._items, item, index, this._size - index);
}
Run Code Online (Sandbox Code Playgroud)
此方法重载使用另一种更常见的重载:
return Array.IndexOf<T>(this._items, item, index, this._size - index);
Run Code Online (Sandbox Code Playgroud)
所以这个方法也用它:
public int IndexOf(T item)
Run Code Online (Sandbox Code Playgroud)
所以如果这段代码是没有意义的:
var list = new List<int>(); /*empty!*/
Console.WriteLine(list.IndexOf(1/*item*/));
Run Code Online (Sandbox Code Playgroud)
会抛出一个Exception. 但是,如果没有这种承认,就无法使用IndexOf使用公共重载的这种重载。