Was ist es?
Diese Ausnahme bedeutet, dass Sie versuchen, mit einem ungültigen Index indexweise auf ein Sammlungselement zuzugreifen. Ein Index ist ungültig, wenn er niedriger als die Untergrenze der Sammlung oder größer oder gleich der Anzahl der darin enthaltenen Elemente ist.
Wenn es geworfen wird
Gegeben ein Array deklariert als:
byte[] array = new byte[4];
Sie können von 0 bis 3 auf dieses Array zugreifen. Werte außerhalb dieses Bereichs IndexOutOfRangeException
werden ausgelöst. Denken Sie daran, wenn Sie ein Array erstellen und darauf zugreifen.
Array-Länge
In C # basieren Arrays normalerweise auf 0. Dies bedeutet, dass das erste Element den Index 0 und das letzte Element den Index hat Length - 1
(wobei Length
die Gesamtzahl der Elemente im Array angegeben ist), sodass dieser Code nicht funktioniert:
array[array.Length] = 0;
Beachten Sie außerdem, dass Sie bei Verwendung eines mehrdimensionalen Arrays, das Sie nicht Array.Length
für beide Dimensionen verwenden können, Folgendes verwenden müssen 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;
}
}
Obergrenze ist nicht inklusive Im folgenden Beispiel erstellen wir ein zweidimensionales Roharray von Color
. Jedes Element stellt ein Pixel dar, die Indizes sind von (0, 0)
bis (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;
}
}
Dieser Code schlägt dann fehl, da das Array auf 0 basiert und das letzte Pixel (unten rechts) im Bild wie folgt lautet pixels[imageWidth - 1, imageHeight - 1]
:
pixels[imageWidth, imageHeight] = Color.Black;
In einem anderen Szenario erhalten Sie möglicherweise ArgumentOutOfRangeException
diesen Code (z. B. wenn Sie eine GetPixel
Methode für eine Bitmap
Klasse verwenden).
Arrays wachsen nicht
Ein Array ist schnell. Sehr schnell in der linearen Suche im Vergleich zu jeder anderen Sammlung. Dies liegt daran, dass Elemente im Speicher zusammenhängend sind, sodass die Speicheradresse berechnet werden kann (und das Inkrement nur eine Ergänzung ist). Keine Notwendigkeit, einer Knotenliste zu folgen, einfache Mathematik! Sie zahlen dies mit einer Einschränkung: Sie können nicht wachsen. Wenn Sie mehr Elemente benötigen, müssen Sie dieses Array neu zuweisen (dies kann relativ lange dauern, wenn alte Elemente in einen neuen Block kopiert werden müssen). Sie ändern die Größe mit Array.Resize<T>()
. In diesem Beispiel wird einem vorhandenen Array ein neuer Eintrag hinzugefügt:
Array.Resize(ref array, array.Length + 1);
Vergessen Sie nicht, dass gültige Indizes von 0
bis sind Length - 1
. Wenn Sie einfach versuchen, ein Element zuzuweisen, erhalten Length
Sie IndexOutOfRangeException
(dieses Verhalten kann Sie verwirren, wenn Sie glauben, dass es mit einer Syntax zunimmt, die der Insert
Methode anderer Sammlungen ähnelt ).
Spezielle Arrays mit benutzerdefinierter Untergrenze Das
erste Element in Arrays hat immer den Index 0 . Dies ist nicht immer der Fall, da Sie ein Array mit einer benutzerdefinierten Untergrenze erstellen können:
var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });
In diesem Beispiel sind Array-Indizes von 1 bis 4 gültig. Natürlich kann die Obergrenze nicht geändert werden.
Falsche Argumente
Wenn Sie mit nicht validierten Argumenten (von Benutzereingaben oder vom Funktionsbenutzer) auf ein Array zugreifen, wird möglicherweise folgende Fehlermeldung angezeigt:
private static string[] RomanNumbers =
new string[] { "I", "II", "III", "IV", "V" };
public static string Romanize(int number)
{
return RomanNumbers[number];
}
Unerwartete Ergebnisse
Diese Ausnahme kann auch aus einem anderen Grund ausgelöst werden: Konventionell geben viele Suchfunktionen -1 zurück (nullables wurde mit .NET 2.0 eingeführt und es ist auch eine bekannte Konvention, die seit vielen Jahren verwendet wird), wenn dies nicht der Fall ist. nichts finden. Stellen Sie sich vor, Sie haben eine Reihe von Objekten, die mit einer Zeichenfolge vergleichbar sind. Sie könnten denken, diesen Code zu schreiben:
// 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")]);
Dies schlägt fehl, wenn keine Elemente in der myArray
Suchbedingung erfüllt sind, da Array.IndexOf()
-1 zurückgegeben wird und der Array-Zugriff ausgelöst wird.
Das nächste Beispiel ist ein naives Beispiel für die Berechnung des Auftretens eines bestimmten Satzes von Zahlen (Kenntnis der maximalen Anzahl und Rückgabe eines Arrays, bei dem Element bei Index 0 für Nummer 0 steht, Elemente bei Index 1 für Nummer 1 usw.):
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;
}
Natürlich ist es eine ziemlich schreckliche Implementierung, aber was ich zeigen möchte, ist, dass es bei negativen Zahlen und Zahlen oben fehlschlägt maximum
.
Wie es gilt List<T>
?
Gleiche Fälle wie Array - Bereich gültiger Indizes - 0 (List
die Indizes beginnen immer mit 0) bis list.Count
- Zugriff auf Elemente außerhalb dieses Bereichs verursachen die Ausnahme.
Beachten Sie, dass List<T>
wirftArgumentOutOfRangeException
für dieselben Fälle gelten, in denen Arrays verwendet werden IndexOutOfRangeException
.
Im Gegensatz zu Arrays wird der List<T>
Start leer. Der Versuch, auf Elemente der gerade erstellten Liste zuzugreifen, führt zu dieser Ausnahme.
var list = new List<int>();
Häufig führt das Auffüllen einer Liste mit Indizierung (ähnlich wie Dictionary<int, T>
) zu einer Ausnahme:
list[0] = 42; // exception
list.Add(42); // correct
IDataReader und Spalten
Stellen Sie sich vor, Sie versuchen, Daten aus einer Datenbank mit diesem Code zu lesen:
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()
wird werfen, IndexOutOfRangeException
weil Ihr Datensatz nur zwei Spalten hat, aber Sie versuchen, einen Wert von der dritten zu erhalten (Indizes sind immer auf 0).
Bitte beachten Sie, dass dieses Verhalten mit den meisten IDataReader
Implementierungen geteilt wird ( SqlDataReader
,OleDbDataReader
usw.).
Sie können dieselbe Ausnahme auch erhalten, wenn Sie die IDataReader-Überladung des Indexeroperators verwenden, der einen Spaltennamen verwendet und einen ungültigen Spaltennamen übergibt.
Angenommen, Sie haben eine Spalte mit dem Namen Column1 abgerufen, versuchen dann aber, den Wert dieses Felds mit abzurufen
var data = dr["Colum1"]; // Missing the n in Column1.
Dies liegt daran, dass der Indexeroperator implementiert ist und versucht, den Index eines Colum1 abzurufen dass nicht vorhandenen Felds . Die GetOrdinal-Methode löst diese Ausnahme aus, wenn ihr interner Hilfscode -1 als Index für "Colum1" zurückgibt.
Andere
Es gibt einen anderen (dokumentierten) Fall, in dem diese Ausnahme ausgelöst wird: Wenn der DataView
Name der Datenspalte an dieDataViewSort
Eigenschaft übergeben wird, ungültig ist.
Wie zu vermeiden
Lassen Sie mich in diesem Beispiel der Einfachheit halber annehmen, dass Arrays immer monodimensional und 0-basiert sind. Wenn Sie streng sein wollen (oder Sie entwickeln eine Bibliothek), müssen Sie ersetzen 0
mit GetLowerBound(0)
und .Length
mit GetUpperBound(0)
(natürlich nur, wenn Sie die Parameter des Typs haben System.Arra
y, gilt sie nicht für T[]
). Bitte beachten Sie, dass in diesem Fall die Obergrenze den folgenden Code enthält:
for (int i=0; i < array.Length; ++i) { }
Sollte folgendermaßen umgeschrieben werden:
for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) { }
Bitte beachten Sie, dass dies nicht zulässig ist (es wird geworfen InvalidCastException
). Wenn Ihre Parameter T[]
sicher sind, können Sie daher benutzerdefinierte Arrays mit niedrigeren Grenzen verwenden:
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 }));
}
Parameter validieren
Wenn der Index von einem Parameter stammt, sollten Sie diese immer validieren (entsprechend werfen ArgumentException
oder ArgumentOutOfRangeException
). Im nächsten Beispiel können falsche Parameter dazu führen IndexOutOfRangeException
, dass Benutzer dieser Funktion dies erwarten, weil sie ein Array übergeben, dies jedoch nicht immer so offensichtlich ist. Ich würde vorschlagen, immer Parameter für öffentliche Funktionen zu validieren:
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);
}
Wenn die Funktion privat ist, können Sie die if
Logik einfach durch Folgendes ersetzen Debug.Assert()
:
Debug.Assert(from >= 0 && from < array.Length);
Check Object State
Array-Index stammt möglicherweise nicht direkt von einem Parameter. Es kann Teil des Objektzustands sein. Im Allgemeinen ist es immer eine gute Praxis, den Objektstatus zu validieren (für sich und bei Bedarf mit Funktionsparametern). Sie können Debug.Assert()
eine geeignete Ausnahme verwenden (das Problem genauer beschreiben) oder wie in diesem Beispiel behandeln:
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];
}
}
Rückgabewerte validieren
In einem der vorherigen Beispiele haben wir den Array.IndexOf()
Rückgabewert direkt verwendet . Wenn wir wissen, dass es fehlschlagen kann, ist es besser, diesen Fall zu behandeln:
int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
So debuggen Sie
Meiner Meinung nach können die meisten Fragen hier auf SO zu diesem Fehler einfach vermieden werden. Die Zeit, die Sie zum Schreiben einer richtigen Frage (mit einem kleinen Arbeitsbeispiel und einer kleinen Erklärung) aufwenden, kann leicht viel mehr sein als die Zeit, die Sie zum Debuggen Ihres Codes benötigen. Lesen Sie zunächst den Blog-Beitrag von Eric Lippert über das Debuggen kleiner Programme . Ich werde seine Worte hier nicht wiederholen, aber es ist absolut ein Muss .
Sie haben Quellcode, Sie haben eine Ausnahmemeldung mit einem Stack-Trace. Gehen Sie dorthin, wählen Sie die richtige Zeilennummer und Sie werden sehen:
array[index] = newValue;
Sie haben Ihren Fehler gefunden, überprüfen Sie, wie sich index
erhöht. Ist es richtig? Überprüfen Sie, wie das Array zugewiesen wird. Stimmt dies mit den index
Erhöhungen überein? Ist es richtig nach Ihren Vorgaben? Wenn Sie mit Ja antworten all diese Fragen mit beantworten, finden Sie hier auf StackOverflow gute Hilfe. Überprüfen Sie dies jedoch zunächst selbst. Sie sparen Ihre eigene Zeit!
Ein guter Ausgangspunkt ist, immer Zusicherungen zu verwenden und Eingaben zu validieren. Möglicherweise möchten Sie sogar Codeverträge verwenden. Wenn etwas schief gelaufen ist und Sie mit einem kurzen Blick auf Ihren Code nicht herausfinden können, was passiert, müssen Sie auf einen alten Freund zurückgreifen: den Debugger . Führen Sie einfach Ihre Anwendung im Debug in Visual Studio (oder Ihrer bevorzugten IDE) aus. Sie sehen genau, welche Zeile diese Ausnahme auslöst, welches Array betroffen ist und welchen Index Sie verwenden möchten. Wirklich, 99% der Fälle lösen Sie es in wenigen Minuten selbst.
Wenn dies in der Produktion passiert, sollten Sie Behauptungen in belasteten Code einfügen. Wahrscheinlich sehen wir in Ihrem Code nicht, was Sie selbst nicht sehen können (aber Sie können immer wetten).
Die VB.NET-Seite der Geschichte
Alles, was wir in der C # -Antwort gesagt haben, gilt für VB.NET mit den offensichtlichen Syntaxunterschieden. Es ist jedoch ein wichtiger Punkt zu beachten, wenn Sie mit VB.NET-Arrays arbeiten.
In VB.NET werden Arrays deklariert, die den maximal gültigen Indexwert für das Array festlegen. Es ist nicht die Anzahl der Elemente, die wir im Array speichern möchten.
' declares an array with space for 5 integer
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer
Diese Schleife füllt das Array also mit 5 Ganzzahlen, ohne eine IndexOutOfRangeException zu verursachen
For i As Integer = 0 To 4
myArray(i) = i
Next
Die VB.NET-Regel
Diese Ausnahme bedeutet, dass Sie versuchen, mit einem ungültigen Index indexweise auf ein Sammlungselement zuzugreifen. Ein Index ist ungültig, wenn er niedriger als die Untergrenze der Sammlung oder größer als istgleich der Anzahl der darin enthaltenen Elemente. Der maximal zulässige Index, der in der Array-Deklaration definiert ist