Mam trochę kodu, a kiedy się wykonuje, rzuca IndexOutOfRangeException
, mówiąc:
Indeks znajdował się poza granicami tablicy.
Co to znaczy i co mogę z tym zrobić?
W zależności od użytych klas może być również ArgumentOutOfRangeException
W mscorlib.dll wystąpił wyjątek typu „System.ArgumentOutOfRangeException”, ale nie został obsłużony w kodzie użytkownika. Informacje dodatkowe: Indeks był poza zakresem. Musi być nieujemny i mniejszy niż rozmiar kolekcji.
c#
.net
indexoutofrangeexception
Adriano Repetti
źródło
źródło
Odpowiedzi:
Co to jest?
Ten wyjątek oznacza, że próbujesz uzyskać dostęp do elementu kolekcji według indeksu, używając nieprawidłowego indeksu. Indeks jest nieprawidłowy, gdy jest niższy niż dolna granica kolekcji lub większy lub równy liczbie elementów w nim zawartych.
Kiedy zostanie rzucony
Biorąc pod uwagę tablicę zadeklarowaną jako:
Możesz uzyskać dostęp do tej tablicy od 0 do 3, wartości poza tym zakresem spowodują
IndexOutOfRangeException
wyrzucenie. Pamiętaj o tym podczas tworzenia i uzyskiwania dostępu do tablicy.Długość tablicy
W języku C # zazwyczaj tablice są oparte na 0. Oznacza to, że pierwszy element ma indeks 0, a ostatni element ma indeks
Length - 1
(gdzieLength
jest łączna liczba elementów w tablicy), więc ten kod nie działa:Ponadto należy pamiętać, że jeśli masz tablicę wielowymiarową, to nie możesz użyć
Array.Length
dla obu wymiarów, musisz użyćArray.GetLength()
:Górna granica nie jest włączająca
W poniższym przykładzie tworzymy surową tablicę dwuwymiarową
Color
. Każdy element reprezentuje piksel, indeksy są od(0, 0)
do(imageWidth - 1, imageHeight - 1)
.Ten kod nie powiedzie się, ponieważ tablica jest oparta na 0, a ostatni (prawy dolny) piksel na obrazie to
pixels[imageWidth - 1, imageHeight - 1]
:W innym scenariuszu możesz otrzymać
ArgumentOutOfRangeException
ten kod (na przykład, jeśli używaszGetPixel
metody naBitmap
klasie).Tablice nie rosną
Tablica jest szybka. Bardzo szybki w wyszukiwaniu liniowym w porównaniu do każdej innej kolekcji. Dzieje się tak, ponieważ elementy są ciągłe w pamięci, dzięki czemu można obliczyć adres pamięci (a przyrost jest tylko dodatkiem). Nie musisz śledzić listy węzłów, prosta matematyka! Płacisz to z ograniczeniem: nie mogą rosnąć, jeśli potrzebujesz więcej elementów, musisz ponownie przydzielić tę tablicę (może to zająć stosunkowo dużo czasu, jeśli stare elementy muszą zostać skopiowane do nowego bloku). Zmieniasz ich rozmiar za pomocą
Array.Resize<T>()
, ten przykład dodaje nowy wpis do istniejącej tablicy:Nie zapominaj, że prawidłowe indeksy są od
0
doLength - 1
. Jeśli po prostu spróbujesz przypisać elementLength
, otrzymaszIndexOutOfRangeException
(to zachowanie może Cię pomylić, jeśli uważasz, że może wzrosnąć przy składni podobnej doInsert
metody z innych kolekcji).Tablice specjalne z niestandardową dolną granicą
Pierwszy element w tablicach ma zawsze indeks 0 . Nie zawsze jest to prawdą, ponieważ możesz utworzyć tablicę z niestandardową dolną granicą:
W tym przykładzie indeksy tablic są prawidłowe od 1 do 4. Oczywiście górnej granicy nie można zmienić.
Błędne argumenty
Jeśli uzyskujesz dostęp do tablicy za pomocą nieważnych argumentów (z danych wprowadzonych przez użytkownika lub od funkcji użytkownika), możesz otrzymać ten błąd:
Nieoczekiwane wyniki
Ten wyjątek może zostać zgłoszony również z innego powodu: zgodnie z konwencją wiele funkcji wyszukiwania zwróci -1 (wprowadzono wartości zerowe w .NET 2.0, a zresztą jest to również znana konwencja używana od wielu lat), jeśli nie nic znaleźć. Wyobraźmy sobie, że masz szereg obiektów porównywalnych z ciągiem znaków. Możesz napisać ten kod:
Nie powiedzie się, jeśli żaden element
myArray
nie spełni warunku wyszukiwania, ponieważArray.IndexOf()
zwróci -1, a następnie dostęp do tablicy zostanie wygenerowany.Następny przykład to naiwny przykład obliczania wystąpień danego zestawu liczb (znajomość liczby maksymalnej i zwrócenie tablicy, w której element o indeksie 0 reprezentuje liczbę 0, elementy o indeksie 1 reprezentuje liczbę 1 itd.):
Oczywiście jest to dość okropna implementacja, ale chcę pokazać, że zawiedzie dla liczb ujemnych i liczb powyżej
maximum
.Jak to dotyczy
List<T>
?Te same przypadki, co w tablicy - zakres prawidłowych indeksów - 0 (
List
indeksy zawsze zaczynają się od 0), aby uzyskaćlist.Count
dostęp do elementów poza tym zakresem spowoduje wyjątek.Zauważ, że
List<T>
rzucaArgumentOutOfRangeException
dla tych samych przypadków, w których używane są tabliceIndexOutOfRangeException
.W przeciwieństwie do tablic,
List<T>
zaczyna się pusty - więc próba dostępu do elementów właśnie utworzonej listy prowadzi do tego wyjątku.Częstym przypadkiem jest zapełnienie listy indeksowaniem (podobnie do
Dictionary<int, T>
) spowoduje wyjątek:IDataReader i kolumny
Wyobraź sobie, że próbujesz odczytać dane z bazy danych za pomocą tego kodu:
GetString()
wyrzuci,IndexOutOfRangeException
ponieważ twój zestaw danych ma tylko dwie kolumny, ale próbujesz uzyskać wartość z trzeciej (indeksy są zawsze oparte na 0).Należy pamiętać, że takie zachowanie jest wspólne z większości
IDataReader
wdrożeń (SqlDataReader
,OleDbDataReader
i tak dalej).Ten sam wyjątek można uzyskać również w przypadku przeciążenia IDataReader operatora indeksującego, który przyjmuje nazwę kolumny i przekazuje niepoprawną nazwę kolumny.
Załóżmy na przykład, że pobrałeś kolumnę o nazwie Kolumna1, ale następnie próbujesz pobrać wartość tego pola za pomocą
Dzieje się tak, ponieważ operator indeksatora jest zaimplementowany, próbując pobrać indeks pola Colum1 , który nie istnieje. Metoda GetOrdinal zgłosi ten wyjątek, gdy wewnętrzny kod pomocnika zwróci -1 jako indeks „Colum1”.
Inne
Istnieje inny (udokumentowany) przypadek, w którym zgłoszony jest ten wyjątek: jeśli, w
DataView
, nazwa kolumny danych podawana doDataViewSort
właściwości jest nieprawidłowa.Jak ominąć
W tym przykładzie przyjmijmy dla uproszczenia, że tablice są zawsze jednowymiarowe i oparte na 0. Jeśli chcesz być surowe (lub jesteś tworzy bibliotekę), może trzeba wymienić
0
zGetLowerBound(0)
i.Length
zGetUpperBound(0)
(oczywiście jeśli masz parametry typuSystem.Arra
Y, to nie dotyczyT[]
). Należy pamiętać, że w tym przypadku górna granica jest włącznie, a następnie ten kod:Powinny zostać przepisane w następujący sposób:
Pamiętaj, że jest to niedozwolone (wyrzuci
InvalidCastException
), dlatego jeśli twoje parametry sąT[]
bezpieczne w przypadku niestandardowych tablic dolnych granic:Sprawdź parametry
Jeśli indeks pochodzi z parametru, powinieneś go zawsze zweryfikować (rzucając odpowiedni
ArgumentException
lubArgumentOutOfRangeException
). W następnym przykładzie mogą wystąpić niepoprawne parametryIndexOutOfRangeException
, użytkownicy tej funkcji mogą się tego spodziewać, ponieważ przekazują tablicę, ale nie zawsze jest to tak oczywiste. Proponuję zawsze sprawdzać poprawność parametrów funkcji publicznych:Jeśli funkcja jest prywatna, możesz po prostu zastąpić
if
logikęDebug.Assert()
:Sprawdź
indeks tablicy stanu obiektu może nie pochodzić bezpośrednio z parametru. Może być częścią stanu obiektu. Zasadniczo zawsze dobrą praktyką jest sprawdzanie stanu obiektu (samo z parametrami funkcji, jeśli to konieczne). Możesz użyć
Debug.Assert()
, rzucić odpowiedni wyjątek (bardziej opisowy na temat problemu) lub obsłużyć go jak w tym przykładzie:Zweryfikuj wartości zwracane
W jednym z poprzednich przykładów bezpośrednio użyliśmy
Array.IndexOf()
wartości zwracanej. Jeśli wiemy, że może się nie powieść, lepiej poradzić sobie z tą sprawą:Jak debugować
Moim zdaniem, większości pytań tutaj, na SO, można po prostu uniknąć. Czas, który poświęcasz na napisanie właściwego pytania (z małym działającym przykładem i małym wyjaśnieniem) może z łatwością znacznie więcej niż czas potrzebny na debugowanie kodu. Przede wszystkim przeczytaj ten post na blogu Erica Lipperta o debugowaniu małych programów. Nie powtórzę tutaj jego słów, ale jest to absolutnie konieczne .
Masz kod źródłowy, masz komunikat wyjątku ze śledzeniem stosu. Idź tam, wybierz odpowiedni numer linii, a zobaczysz:
Znalazłeś błąd, sprawdź, jak
index
rośnie. Czy to jest poprawne? Sprawdź, w jaki sposób alokowana jest tablica, czy jest spójna zeindex
wzrostem? Czy to zgodne z twoimi specyfikacjami? Jeśli odpowiesz tak na wszystkie te pytania, znajdziesz tutaj pomoc na StackOverflow, ale najpierw sprawdź ją samodzielnie. Zaoszczędzisz swój czas!Dobrym punktem wyjścia jest zawsze stosowanie asercji i sprawdzanie poprawności danych wejściowych. Możesz nawet chcieć skorzystać z umów kodowych. Kiedy coś poszło nie tak i nie możesz dowiedzieć się, co dzieje się z szybkim spojrzeniem na swój kod, musisz skorzystać ze starego znajomego: debuggera . Wystarczy uruchomić aplikację podczas debugowania w programie Visual Studio (lub ulubionym środowisku IDE), zobaczysz dokładnie, która linia generuje ten wyjątek, która tablica jest zaangażowana i jakiego indeksu próbujesz użyć. Naprawdę, 99% razy rozwiążesz go samodzielnie w ciągu kilku minut.
Jeśli tak się stanie w produkcji, lepiej dodaj twierdzenia do obciążonego kodu, prawdopodobnie nie zobaczymy w twoim kodzie tego, czego sam nie widzisz (ale zawsze możesz postawić).
Strona VB.NET tej historii
Wszystko, co powiedzieliśmy w odpowiedzi w języku C #, jest ważne dla VB.NET z oczywistymi różnicami w składni, ale należy wziąć pod uwagę ważną kwestię, mając do czynienia z tablicami VB.NET.
W VB.NET tablice deklarują ustawienie maksymalnej poprawnej wartości indeksu dla tablicy. To nie jest liczba elementów, które chcemy przechowywać w tablicy.
Zatem ta pętla wypełni tablicę 5 liczbami całkowitymi bez powodowania żadnego wyjątku IndexOutOfRangeException
Reguła VB.NET
Ten wyjątek oznacza, że próbujesz uzyskać dostęp do elementu kolekcji według indeksu, używając nieprawidłowego indeksu. Indeks jest nieprawidłowy, gdy jest niższy niż dolna granica kolekcji lub większy niż
równa liczbie zawartych w nim elementów.maksymalny dozwolony indeks zdefiniowany w deklaracji tablicowejźródło
Proste wyjaśnienie, czym jest wyjątek poza ograniczonym indeksem:
Pomyśl tylko o jednym pociągu, którego przedziały to D1, D2, D3. Jeden pasażer przyszedł, aby wsiąść do pociągu i ma bilet na D4. teraz co się stanie. pasażer chce wejść do przedziału, który nie istnieje, więc oczywiście pojawią się problemy.
Ten sam scenariusz: ilekroć próbujemy uzyskać dostęp do listy tablic itp., Możemy uzyskać dostęp tylko do istniejących indeksów w tablicy.
array[0]
iarray[1]
istnieją. Jeśli spróbujemy uzyskać dostęparray[3]
, nie ma go tak naprawdę, więc powstanie indeks poza ograniczonym wyjątkiem.źródło
Aby łatwo zrozumieć problem, wyobraź sobie, że napisaliśmy ten kod:
Wynik będzie:
Rozmiar tablicy wynosi 3 (indeksy 0, 1 i 2), ale pętla for-loop 4 razy (0, 1, 2 i 3).
Kiedy więc próbuje uzyskać dostęp poza granicami za pomocą (3), zgłasza wyjątek.
źródło
Oprócz bardzo długiej, kompletnej, zaakceptowanej odpowiedzi należy zwrócić uwagę na
IndexOutOfRangeException
wiele innych typów wyjątków, a mianowicie:Często występuje złożony stan programu, który może być trudny do kontrolowania w określonym punkcie kodu, np. Połączenie DB zostaje przerwane, więc danych wejściowych nie można odzyskać itp. Tego rodzaju problem często powoduje wyjątek, który musi podnieść się na wyższy poziom, ponieważ tam, gdzie ma to miejsce, nie ma sposobu na poradzenie sobie z tym w tym momencie.
IndexOutOfRangeException
ogólnie różni się tym, że w większości przypadków sprawdzanie w momencie zgłaszania wyjątku jest dość trywialne. Zasadniczo ten wyjątek generowany jest przez jakiś kod, który bardzo łatwo poradziłby sobie z problemem w miejscu, w którym się pojawia - po prostu sprawdzając rzeczywistą długość tablicy. Nie chcesz tego „naprawiać”, obsługując ten wyjątek wyżej - ale zamiast tego upewniając się, że nie zostanie zgłoszony w pierwszej instancji - co w większości przypadków jest łatwe do wykonania przez sprawdzenie długości tablicy.Innym sposobem na określenie tego jest to, że mogą wystąpić inne wyjątki z powodu rzeczywistego braku kontroli nad wejściem lub stanem programu, ALE
IndexOutOfRangeException
częściej niż po prostu błąd pilota (programisty).źródło