c# - ¿Qué es una IndexOutOfRangeException/ArgumentOutOfRangeException y cómo lo soluciono?

CorePress2024-01-25  12

Tengo un código y cuando se ejecuta, arroja una excepción IndexOutOfRangeException que dice:

El índice estaba fuera de los límites de la matriz.

¿Qué significa esto y qué puedo hacer al respecto?

Dependiendo de las clases utilizadas también puede ser ArgumentOutOfRangeException

Se produjo una excepción de tipo 'System.ArgumentOutOfRangeException' en mscorlib.dll pero no se manejó en el código de usuario Información adicional: el índice estaba fuera de rango. Debe ser no negativo y menor que el tamaño de la colección.

En tu colecciónsi solo tiene 4 elementos, pero el código intentó obtener un elemento en el índice 5. Esto generará IndexOutOfRangeException. Índice de verificación = 5; if(items.Length >= index ) Console.WriteLine(intems[index ]);

- Babu Kumarasamy

29 de junio de 2020 a las 5:47



------------------------------------

¿Qué es?

Esta excepción significa que estás intentando acceder a un elemento de la colección por índice, utilizando un índice no válido. Un índice no es válido cuando es inferior al límite inferior de la colección o mayor o igual al número de elementos que contiene.

cuando se tira

Dadouna matriz declarada como:

byte[] array = new byte[4];

Puede acceder a esta matriz de 0 a 3; los valores fuera de este rango provocarán que se genere IndexOutOfRangeException. Recuerde esto cuando cree y acceda a una matriz.

Longitud de la matriz En C#, normalmente, las matrices están basadas en 0. Significa que el primer elemento tiene índice 0 y el último elemento tiene índice Longitud - 1 (donde Longitud es el número total de elementos en la matriz), por lo que este código no funciona:

array[array.Length] = 0;

Además, tenga en cuenta que si tiene una matriz multidimensional, no puede usar Array.Length para ambas dimensiones, debe usar 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;
    }
}

El límite superior no es inclusivo En el siguiente ejemplo creamos una matriz bidimensional sin formato de Color. Cada elemento representa un píxel, los índices van de (0, 0) a (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;
    }
}

Este código fallará porque la matriz está basada en 0 y el último píxel (abajo a la derecha) de la imagen es píxeles[imageWidth - 1, imageHeight - 1]:

pixels[imageWidth, imageHeight] = Color.Black;

En otro escenario, puede obtener ArgumentOutOfRangeException para este código (por ejemplo, si está utilizando el método GetPixel en una clase Bitmap).

Las matrices no crecen Una matriz es rápida. Muy rápido en búsqueda lineal en comparación con cualquier otra colección. Debido a que los elementos son contiguos en la memoria, se puede calcular la dirección de la memoria (y el incremento es solo una suma). No es necesario seguir una lista de nodos, ¡matemáticas simples! Paga esto con una limitación: no pueden crecer, si necesita más elementos necesita reasignar esa matriz (esto puede llevar un tiempo relativamente largo si los elementos antiguos deben copiarse en un bloque nuevo). Los cambias de tamaño con Array.Resize<T>(), este ejemplo agrega una nueva entrada a una matriz existente:

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

No olvide que los índices válidos van de 0 a Longitud - 1. Si simplemente intenta asignar un elemento en Longitud obtendrá IndexOutOfRangeException (este comportamiento puede confundirlo si cree que pueden aumentar con una sintaxis similar a Insertar método de otras colecciones).

Matrices especiales con límite inferior personalizado El primer elemento de las matrices siempre tiene el índice 0. Esto no siempre es cierto porque puedes crear una matriz con un límite inferior personalizado:

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

En ese ejemplo, los índices de matriz son válidos del 1 al 4. Por supuesto, el límite superior no se puede cambiar.

Argumentos incorrectos Si accede a una matriz utilizando argumentos no validados (desde la entrada del usuario o desde la función del usuario), puede obtener este error:

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

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

InesperadoResultados Esta excepción también puede deberse a otra razón: por convención, muchas funciones de búsqueda devolverán -1 (los valores nulos se introdujeron con .NET 2.0 y, de todos modos, también es una convención bien conocida que se utiliza desde hace muchos años) si no encontraron cualquier cosa. Imaginemos que tiene una serie de objetos comparables a una cadena. Puedes pensar en escribir este código:

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

Esto fallará si ningún elemento en myArray satisface la condición de búsqueda porque Array.IndexOf() devolverá -1 y luego se arrojará el acceso a la matriz.

El siguiente ejemplo es un ejemplo ingenuo para calcular las apariciones de un conjunto determinado de números (conociendo el número máximo y devolviendo una matriz donde el elemento en el índice 0 representa el número 0, los elementos en el índice 1 representan el número 1 y así sucesivamente):

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

Por supuesto, es bastante terrible.implementación, pero lo que quiero mostrar es que fallará para números negativos y números por encima del máximo.

¿Cómo se aplica a List<T>?

Los mismos casos que en matriz - rango de índices válidos - 0 (los índices de la lista siempre comienzan con 0) a list.Count - acceder a elementos fuera de este rango causará la excepción.

Tenga en cuenta que Lista<T> lanza ArgumentOutOfRangeException para los mismos casos en los que las matrices usan IndexOutOfRangeException.

A diferencia de las matrices, List<T> comienza vacío, por lo que intentar acceder a elementos de la lista recién creada genera esta excepción.

var list = new List<int>();

El caso común es completar la lista con indexación (similar al Diccionario<int, T>) y causará una excepción:

list[0] = 42; // exception
list.Add(42); // correct

IDataReader y columnas Imagina que estás intentando leer datos de una base de datos con esto.código s:

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() arrojará IndexOutOfRangeException porque su conjunto de datos tiene solo dos columnas pero está intentando obtener un valor de la tercera (los índices siempre están basados ​​en 0).

Tenga en cuenta que este comportamiento se comparte con la mayoría de las implementaciones de IDataReader (SqlDataReader, OleDbDataReader, etc.).

También puede obtener la misma excepción si utiliza la sobrecarga IDataReader del operador indexador que toma un nombre de columna y pasa un nombre de columna no válido. Supongamos, por ejemplo, que ha recuperado una columna denominada Columna1 pero luego intenta recuperar el valor de ese campo con

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

Esto sucede porque el operador indexador se implementa intentando recuperar el índice de un campo Colum1 que no existe. El método GetOrdinal generará esta excepción.cuando su código auxiliar interno devuelve un -1 como índice de "Columna1".

Otros Hay otro caso (documentado) en el que se produce esta excepción: si, en DataView, el nombre de la columna de datos que se proporciona a la propiedad DataViewSort no es válido.

Como evitar

En este ejemplo, permítanme asumir, por simplicidad, que las matrices son siempre monodimensionales y basadas en 0. Si quiere ser estricto (o está desarrollando una biblioteca), es posible que necesite reemplazar 0 con GetLowerBound(0) y .Length con GetUpperBound(0) (por supuesto, si tiene parámetros de tipo System.Array, no No se aplica a T[]). Tenga en cuenta que, en este caso, el límite superior incluye este código:

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

Debería reescribirse así:

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

Tenga en cuenta que esto no está permitido (arrojará InvalidCastException),es por eso que si sus parámetros son T[], está seguro acerca de las matrices de límite inferior personalizadas:

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

Validar parámetros Si el índice proviene de un parámetro, siempre debe validarlo (lanzando una ArgumentException o ArgumentOutOfRangeException apropiada). En el siguiente ejemplo, los parámetros incorrectos pueden causar IndexOutOfRangeException; los usuarios de esta función pueden esperar esto porque están pasando una matriz, pero no siempre es tan obvio. Sugeriría validar siempre los parámetros para funciones públicas:

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

Si la función es privada, simplemente puede reemplazar la lógica if con Debug.Assert():

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

Comprobar estado del objeto Es posible que el índice de matriz no provenga directamente de un parámetro. Puede ser parte del estado del objeto. En general, siempre es una buena práctica validar el estado del objeto (por sí mismo y con funciones).parámetros de iones, si es necesario). Puedes usar Debug.Assert(), lanzar una excepción adecuada (más descriptiva sobre el problema) o manejar eso como en este ejemplo:

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

Validar valores devueltos En uno de los ejemplos anteriores utilizamos directamente el valor de retorno de Array.IndexOf(). Si sabemos que puede fallar entonces es mejor manejar ese caso:

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

En mi opinión, la mayoría de las preguntas, aquí en SO, sobre este error pueden simplemente evitarse. El tiempo que dedica a escribir una pregunta adecuada (con un pequeño ejemplo práctico y una pequeña explicación) fácilmente podría ser mucho mayor que el tiempo que necesitará para depurar su código. En primer lugar, lea esta publicación de blog de Eric Lippert sobre la depuración de programas pequeños. No repetiré sus palabras aquí, pero es una lectura absolutamente obligada.

Tienes el código fuente, yTienes un mensaje de excepción con un seguimiento de la pila. Vaya allí, elija el número de línea correcto y verá:

array[index] = newValue;

Encontraste tu error, comprueba cómo aumenta el índice. ¿Es correcto? Compruebe cómo se asigna la matriz, ¿es coherente con cómo aumenta el índice? ¿Está bien según sus especificaciones? Si responde afirmativamente a todas estas preguntas, encontrará buena ayuda aquí en StackOverflow, pero primero compruébelo usted mismo. ¡Ahorrarás tu propio tiempo!

Un buen punto de partida es utilizar siempre afirmaciones y validar las entradas. Es posible que incluso desee utilizar contratos de código. Cuando algo salió mal y no puedes entender qué sucede con un vistazo rápido a tu código, entonces tienes que recurrir a un viejo amigo: el depurador. Simplemente ejecute su aplicación en depuración dentro de Visual Studio (o su IDE favorito),Verás exactamente qué línea arroja esta excepción, qué matriz está involucrada y qué índice estás intentando usar. Realmente, el 99% de las veces lo solucionarás tú solo en unos minutos.

Si esto sucede en producción, entonces será mejor que agregues aserciones en el código incriminado, probablemente no veremos en tu código lo que tú no puedes ver por ti mismo (pero siempre puedes apostar).

El lado VB.NET de la historia

Todo lo que hemos dicho en la respuesta de C# es válido para VB.NET con las diferencias de sintaxis obvias, pero hay un punto importante a considerar cuando se trata de matrices de VB.NET.

En VB.NET, las matrices se declaran estableciendo el valor de índice máximo válido para la matriz. No es el recuento de los elementos que queremos almacenar en el 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

Así que este bucle llenará el ar.rayo con 5 enteros sin causar ninguna IndexOutOfRangeException

For i As Integer = 0 To 4
    myArray(i) = i
Next
La regla VB.NET

Esta excepción significa que estás intentando acceder a un elemento de la colección por índice, utilizando un índice no válido. Un índice no es válido cuando es inferior al límite inferior de la colección o mayor que el número de elementos que contiene. el índice máximo permitido definido en la declaración de matriz

Respondido al

4 de marzo de 2016 a las 11:22

Lijo

Lijo

6,578

5

5 insignias de oro

50

50 insignias de plata

60

60 insignias de bronce

0



------------------------------------

Para comprender fácilmente el problema, imagina que escribimos este código:

static void Main(string[] args)
{
    string[] test = new string[3];
    test[0]= "hello1";
    test[1]= "hello2";
    test[2]= "hello3";

    for (int i = 0; i <= 3; i++)
    {
        Console.WriteLine(test[i].ToString());
    }
}

El resultado será:

hello1
hello2
hello3

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.

El tamaño de la matriz es 3 (índices 0, 1 y 2), pero el bucle for se repite 4 veces (0, 1, 2 y 3). Entonces, cuando intenta acceder fuera de los límites con (3), arroja la excepción.

Respondido al

7 de diciembre de 2017 a las 6:48

sonertbnc

sonertbnc

1.660

16

16 insignias de plata

27

27 insignias de bronce

0



------------------------------------

Además de la larga respuesta completa aceptada, hay un punto importante que destacar sobre IndexOutOfRangeException en comparación con muchos otros tipos de excepción, y es:

A menudo hay un estado de programa complejo sobre el que tal vez sea difícil tener control en un punto particular del código, por ejemplo, una conexión a la base de datos se cae, por lo que no se pueden recuperar los datos de una entrada, etc. Este tipo de problema a menudo resulta en una excepción de algún tipo que tiene que burbujear a un nivel más alto porque donde ocurre no hay forma de abordarlo en ese momento.

IndexOutOfRangeException generalmente es diferenteEs diferente porque en la mayoría de los casos es bastante trivial comprobarlo en el punto donde se genera la excepción. Generalmente, este tipo de excepción es generada por algún código que podría resolver fácilmente el problema en el lugar donde ocurre, simplemente verificando la longitud real de la matriz. No desea "arreglar" esto manejando esta excepción en un nivel superior, sino asegurándose de que no se genere en primera instancia, lo cual en la mayoría de los casos es fácil de hacer al verificar la longitud de la matriz.

Otra forma de decir esto es que pueden surgir otras excepciones debido a una falta genuina de control sobre la entrada o el estado del programa, PERO IndexOutOfRangeException la mayoría de las veces es simplemente un error del piloto (programador).



------------------------------------

Estas dos excepciones son comunes en varios lenguajes de programación y, como otros dijeron, es cuando se accede a un elemento con un índice mayor que el tamaño de la matriz. Por ejemplo:

var array = [1,2,3];
/* var lastElement = array[3] this will throw an exception, because indices 
start from zero, length of the array is 3, but its last index is 2. */ 

La razón principal detrás de esto es que los compiladores generalmente no verifican estas cosas, por lo tanto, solo se expresarán en tiempo de ejecución.

Similar a esto: ¿Por qué los compiladores modernos no detectan los intentos de realizar accesos fuera de los límites a las matrices?

2

Si el tamaño de la matriz y/o el índice al que se accede no se conocen hasta el tiempo de ejecución, entonces el compilador no puede verificar que el índice sea válido. De lo contrario, esto no agrega ninguna información que no esté cubierta por las otras respuestas.

- Lance U. Matthews

19 de febrero de 2022 a las 11:59

Sí, pero en el caso de un tamaño de matriz conocido, el compilador tampoco hace nada. Al menos en C++, Java y C#, creo.

- Armin Fisher

19/02/2022 a las 12:04

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare