c# - obtener - ¿Por qué List IndexOf permite el índice de inicio fuera de rango?
obtener el indice de una lista c# (5)
Creo, entiendo por qué. Es un poco más fácil el de la realización de métodos como este. Mira:
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);
}
Esta sobrecarga del método usa otra sobrecarga más común:
return Array.IndexOf<T>(this._items, item, index, this._size - index);
Así que este método también lo usamos:
public int IndexOf(T item)
Así que no tiene sentido si este código:
var list = new List<int>(); /*empty!*/
Console.WriteLine(list.IndexOf(1/*item*/));
lanzará una Exception
. Pero no hay manera de usar esta sobrecarga de IndexOf
usando una sobrecarga común sin esta admisión.
¿Por qué List<T>.IndexOf
permite el índice de inicio fuera de rango?
var list = new List<int>() { 100 };
Console.WriteLine(list.IndexOf(1/*item*/, 1/*start index*/));
No habrá excepciones. ¡Pero no hay ningún artículo con 1
índice en esta colección! Sólo hay un elemento con 0
índice. Entonces, ¿por qué .Net
te permite hacerlo?
En primer lugar, si alguien debería ocuparse de una entrada no válida es el tiempo de ejecución y no el compilador, ya que la entrada es del mismo tipo válido ( int
).
Dicho esto, en realidad, ver el código fuente de IndexOf
haciendo que parezca un error de implementación:
[__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);
}
Como puede ver, estaba destinado a no permitirle insertar un índice no válido que es más grande que el tamaño de la lista, pero la comparación se realiza con >
lugar de >=
.
El siguiente código devuelve
0
:var list = new List<int>() { 100 }; Console.WriteLine(list.IndexOf(100/*item*/, 0/*start index*/));
El siguiente código devuelve
-1
:var list = new List<int>() { 100 }; Console.WriteLine(list.IndexOf(100/*item*/, 1/*start index*/));
Mientras que el siguiente código lanza una
Exception
:var list = new List<int>() { 100 }; Console.WriteLine(list.IndexOf(100/*item*/, 2/*start index*/));
No hay ninguna razón para que el segundo y el tercer caso se comporten de manera diferente, lo que hace que parezca un error en la implementación de IndexOf
.
Además, IndexOf :
ArgumentOutOfRangeException | El índice está fuera del rango de índices válidos para la
List<T>
.
Lo que como acabamos de ver no es lo que sucede .
Nota: el mismo comportamiento ocurre con las matrices:
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*/));
Lo permite porque alguien decidió que esto estaba bien y que alguien escribió la especificación o implementó el método.
También está algo documentado en el List<T>.IndexOf :
0 (cero) es válido en una lista vacía.
(que también tomo que Count
es un índice de inicio válido para cualquier lista)
Tenga en cuenta que lo mismo está documentado, pero un poco mejor documentado, para el método Array.IndexOf
:
Si
startIndex
es igual aArray.Length
, el método devuelve -1. SistartIndex
es mayor queArray.Length
, el método lanza unaArray.Length
ArgumentOutOfRangeException
.
Déjame aclarar mi respuesta aquí.
Estás preguntando "¿Por qué este método permite esta entrada".
La única razón legal es "porque alguien implementó el método para que lo hiciera".
¿Es un error? Muy bien puede ser. La documentación solo indica que 0 es el índice de inicio legal para una lista vacía, no dice directamente que 1 es legal o no para una lista con un solo elemento. La documentación de excepción para el método parece contradecir esto (como se ha mencionado en los comentarios) que parece estar a favor de que sea un error.
Pero la única razón para "por qué hace esto" es que alguien realmente implementó el método de esa manera. Puede ser una elección consciente, puede ser un error, puede ser una omisión en el código o en la documentación.
El único que puede decir cuál es la persona o personas que implementaron este método.
Para entender lo que está pasando podemos echar un vistazo a las sources :
public int IndexOf(T item, int index) {
if (index > _size)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
Contract.Ensures(Contract.Result<int>() >= -1);
Contract.Ensures(Contract.Result<int>() < Count);
Contract.EndContractBlock();
return Array.IndexOf(_items, item, index, _size - index);
}
_size
aquí es equivalente a la lista. list.Count
para que cuando tengas un elemento, puedas usar un index
de 1
incluso si no existe en la lista.
A menos que haya una razón especial por la que no estoy viendo, esto parece ser un buen error en el marco. La documentación incluso menciona que se deben lanzar excepciones si
index
está fuera del rango de índices válidos para laList<T>
.
Por supuesto, el único que puede decir con seguridad por qué es esta es la gente que tomó esa decisión.
La única razón lógica que veo (y esa es mi suposición) es permitir un uso como este
for (int index = list.IndexOf(value); index >= 0; index = list.IndexOf(value, index + 1))
{
// do something
}
o en otras palabras, para poder reiniciar de forma segura la búsqueda desde el siguiente índice de la última búsqueda exitosa .
Esto puede no parecer un escenario muy común, pero es un patrón típico al procesar cadenas (por ejemplo, cuando uno quiere evitar la Split
). Lo que me recuerda que String.IndexOf tiene el mismo comportamiento y está un poco mejor documentado (aunque sin especificar el motivo):
El parámetro startIndex puede variar desde 0 hasta la longitud de la instancia de cadena. Si startIndex es igual a la longitud de la instancia de cadena, el método devuelve -1.
Para reanudar, dado que Array
, string
y List<T>
comparten el mismo comportamiento, aparentemente está pensado y definitivamente no es un error de implementación.