O que é isso?
Essa exceção significa que você está tentando acessar um item de coleção por índice, usando um índice inválido. Um índice é inválido quando é menor que o limite inferior da coleção ou maior que ou igual ao número de elementos que ela contém.
Quando é lançado
Dada uma matriz declarada como:
byte[] array = new byte[4];
Você pode acessar esse array de 0 a 3, valores fora desse intervalo causarão o IndexOutOfRangeException
lançamento. Lembre-se disso ao criar e acessar uma matriz.
Comprimento da matriz
Em C #, geralmente, as matrizes são baseadas em 0. Isso significa que o primeiro elemento tem índice 0 e o último elemento tem índice Length - 1
(onde Length
é o número total de itens na matriz), portanto, esse código não funciona:
array[array.Length] = 0;
Além disso, observe que, se você tiver um array multidimensional, não poderá usar as Array.Length
duas dimensões, precisará 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;
}
}
O limite superior não é inclusivo
No exemplo a seguir, criamos uma matriz bidimensional bruta de Color
. Cada item representa um pixel, os índices são de (0, 0)
para (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;
}
}
Esse código falhará porque a matriz é baseada em 0 e o último pixel (canto inferior direito) da imagem é pixels[imageWidth - 1, imageHeight - 1]
:
pixels[imageWidth, imageHeight] = Color.Black;
Em outro cenário, você pode obter ArgumentOutOfRangeException
esse código (por exemplo, se você estiver usando o GetPixel
método em uma Bitmap
classe).
Matrizes não crescem
Uma matriz é rápida. Muito rápido na pesquisa linear em comparação com todas as outras coleções. Como os itens são contíguos na memória, o endereço da memória pode ser calculado (e o incremento é apenas uma adição). Não há necessidade de seguir uma lista de nós, matemática simples! Você paga isso com uma limitação: eles não podem crescer; se você precisar de mais elementos, precisará realocar essa matriz (isso pode levar um tempo relativamente longo se itens antigos precisarem ser copiados para um novo bloco). Você as redimensiona Array.Resize<T>()
, este exemplo adiciona uma nova entrada a uma matriz existente:
Array.Resize(ref array, array.Length + 1);
Não esqueça que os índices válidos são de 0
para Length - 1
. Se você simplesmente tentar atribuir um item ao Length
seu destino IndexOutOfRangeException
(esse comportamento pode confundir você, se você acha que eles podem aumentar com uma sintaxe semelhante ao Insert
método de outras coleções).
Matrizes especiais com limite inferior personalizado O
primeiro item das matrizes sempre indexa 0 . Isso nem sempre é verdade porque você pode criar uma matriz com um limite inferior personalizado:
var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });
Nesse exemplo, os índices da matriz são válidos de 1 a 4. Obviamente, o limite superior não pode ser alterado.
Argumentos errados
Se você acessar uma matriz usando argumentos não validados (da entrada do usuário ou do usuário da função), poderá receber este erro:
private static string[] RomanNumbers =
new string[] { "I", "II", "III", "IV", "V" };
public static string Romanize(int number)
{
return RomanNumbers[number];
}
Resultados inesperados
Essa exceção também pode ser lançada por outro motivo: por convenção, muitas funções de pesquisa retornarão -1 (nulos foram introduzidos no .NET 2.0 e, de qualquer forma, também é uma convenção conhecida em uso há muitos anos), se não ocorreram ' Não encontrei nada. Vamos imaginar que você tenha uma variedade de objetos comparáveis a uma string. Você pode escrever esse 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")]);
Isso falhará se nenhum item dentro myArray
satisfizer a condição de pesquisa porque Array.IndexOf()
retornará -1 e o acesso ao array será lançado.
O próximo exemplo é um exemplo ingênuo para calcular ocorrências de um determinado conjunto de números (saber o número máximo e retornar uma matriz em que o item no índice 0 representa o número 0, os itens no índice 1 representam o número 1 e assim por diante):
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;
}
Claro, é uma implementação bastante terrível, mas o que eu quero mostrar é que ela falhará nos números negativos e acima maximum
.
Como isso se aplica List<T>
?
Os mesmos casos que o intervalo de matriz de índices válidos - 0 ( List
os índices de sempre começam com 0) list.Count
- o acesso a elementos fora desse intervalo causará a exceção.
Observe que List<T>
lança ArgumentOutOfRangeException
para os mesmos casos em que as matrizes são usadas IndexOutOfRangeException
.
Ao contrário de matrizes, List<T>
começa vazio - portanto, tentar acessar itens da lista recém-criada leva a essa exceção.
var list = new List<int>();
O caso comum é preencher a lista com indexação (semelhante a Dictionary<int, T>
) causará exceção:
list[0] = 42; // exception
list.Add(42); // correct
IDataReader e colunas
Imagine que você está tentando ler dados de um banco de dados com este código:
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()
será lançado IndexOutOfRangeException
porque o conjunto de dados tem apenas duas colunas, mas você está tentando obter um valor da terceira (os índices sempre são baseados em 0).
Por favor note que este comportamento é compartilhada com a maioria das IDataReader
implementações ( SqlDataReader
, OleDbDataReader
e assim por diante).
Você pode obter a mesma exceção também se usar a sobrecarga IDataReader do operador do indexador que pega um nome de coluna e passa um nome de coluna inválido.
Suponha, por exemplo, que você tenha recuperado uma coluna chamada Coluna1, mas tente recuperar o valor desse campo com
var data = dr["Colum1"]; // Missing the n in Column1.
Isso acontece porque o operador do indexador é implementado tentando recuperar o índice de um campo Colum1 que não existe. O método GetOrdinal lançará essa exceção quando seu código auxiliar interno retornar -1 como o índice de "Colum1".
Outros
Há outro caso (documentado) em que essa exceção é lançada: se, em DataView
, o nome da coluna de dados fornecido à DataViewSort
propriedade não for válido.
Como evitar
Neste exemplo, deixe-me supor, por simplicidade, que as matrizes são sempre monodimensionais e baseadas em 0. Se você quer ser rigoroso (ou está desenvolvendo uma biblioteca), pode ser necessário substituir 0
por GetLowerBound(0)
e .Length
por GetUpperBound(0)
(é claro que se você tiver parâmetros do tipo System.Arra
y, isso não se aplica T[]
). Observe que, nesse caso, o limite superior é inclusivo e, em seguida, este código:
for (int i=0; i < array.Length; ++i) { }
Deve ser reescrito assim:
for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) { }
Observe que isso não é permitido (será lançado InvalidCastException
); é por isso que, se seus parâmetros estiverem T[]
seguros, você poderá usar matrizes de limite 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
Se o índice der origem de um parâmetro, você sempre deve validá-los (jogando apropriado ArgumentException
ou ArgumentOutOfRangeException
). No próximo exemplo, parâmetros errados podem causar IndexOutOfRangeException
, os usuários dessa função podem esperar isso porque estão passando uma matriz, mas nem sempre é tão óbvio. Eu sugeriria sempre validar parâmetros para funções 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);
}
Se a função é privada, você pode simplesmente substituir a if
lógica por Debug.Assert()
:
Debug.Assert(from >= 0 && from < array.Length);
O
índice Check Object State Array pode não vir diretamente de um parâmetro. Pode fazer parte do estado do objeto. Em geral, é sempre uma boa prática validar o estado do objeto (por si só e com parâmetros de função, se necessário). Você pode usar Debug.Assert()
, lançar uma exceção adequada (mais descritiva sobre o problema) ou lidar com isso como neste exemplo:
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 de retorno
Em um dos exemplos anteriores, usamos diretamente o Array.IndexOf()
valor de retorno. Se sabemos que pode falhar, é melhor lidar com esse caso:
int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
Como depurar
Na minha opinião, a maioria das perguntas, aqui no SO, sobre esse erro pode ser simplesmente evitada. O tempo que você gasta para escrever uma pergunta adequada (com um pequeno exemplo de trabalho e uma pequena explicação) pode facilmente muito mais do que o tempo necessário para depurar seu código. Antes de tudo, leia este post de Eric Lippert no blog sobre depuração de pequenos programas , não repetirei suas palavras aqui, mas é absolutamente uma leitura obrigatória .
Você tem código fonte, mensagem de exceção com um rastreamento de pilha. Vá para lá, escolha o número da linha direita e você verá:
array[index] = newValue;
Você encontrou seu erro, verifique como index
aumenta. Está certo? Verifique como a matriz é alocada, é coerente com a forma como index
aumenta? Está certo de acordo com suas especificações? Se você responder sim a todas essas perguntas, encontrará uma boa ajuda aqui no StackOverflow, mas verifique isso sozinho. Você economizará seu próprio tempo!
Um bom ponto de partida é sempre usar asserções e validar entradas. Você pode até querer usar contratos de código. Quando algo deu errado e você não consegue descobrir o que acontece com uma rápida olhada no seu código, é necessário recorrer a um velho amigo: o depurador . Basta executar seu aplicativo em depuração no Visual Studio (ou seu IDE favorito), você verá exatamente qual linha lança essa exceção, qual matriz está envolvida e qual índice você está tentando usar. Realmente, 99% das vezes você resolverá sozinho em alguns minutos.
Se isso acontecer na produção, é melhor adicionar asserções no código incriminado, provavelmente não veremos no seu código o que você não pode ver por si mesmo (mas você sempre pode apostar).
O lado VB.NET da história
Tudo o que dissemos na resposta C # é válido para o VB.NET com as óbvias diferenças de sintaxe, mas há um ponto importante a ser considerado quando você lida com matrizes do VB.NET.
No VB.NET, as matrizes são declaradas definindo o valor máximo de índice válido para a matriz. Não é a contagem dos elementos que queremos armazenar na matriz.
' declares an array with space for 5 integer
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer
Portanto, esse loop preencherá a matriz com 5 números inteiros sem causar nenhum IndexOutOfRangeException
For i As Integer = 0 To 4
myArray(i) = i
Next
A regra do VB.NET
Essa exceção significa que você está tentando acessar um item de coleção por índice, usando um índice inválido. Um índice é inválido quando é menor que o limite inferior da coleção ou maior queigual ao número de elementos que ele contém. o índice máximo permitido definido na declaração da matriz