17 tablic
17.1 Ogólne
Tablica to struktura danych zawierająca wiele zmiennych, które są dostępne za pośrednictwem obliczonych indeksów. Zmienne zawarte w tablicy, nazywane również elementami tablicy, są tego samego typu, a ten typ jest nazywany typem elementu tablicy.
Tablica ma rangę, która określa liczbę indeksów skojarzonych z każdym elementem tablicy. Ranga tablicy jest również określana jako wymiary tablicy. Tablica o klasyfikacji jednej jest nazywana tablicą jednowymiarową. Tablica o klasyfikacji większej niż jedna jest nazywana tablicą wielowymiarową. Określone wielowymiarowe tablice są często określane jako tablice dwuwymiarowe, tablice trójwymiarowe itd. Każdy wymiar tablicy ma skojarzona długość, która jest liczbą całkowitą większą lub równą zero. Długości wymiarów nie są częścią typu tablicy, ale są ustanawiane, gdy wystąpienie typu tablicy jest tworzone w czasie wykonywania. Długość wymiaru określa prawidłowy zakres indeksów dla tego wymiaru: w przypadku wymiaru długości N
indeksy mogą wahać się od 0
do N – 1
inkluzywnego. Całkowita liczba elementów w tablicy jest produktem długości każdego wymiaru w tablicy. Jeśli co najmniej jeden wymiar tablicy ma długość zero, tablica jest uważana za pustą.
Typ elementu tablicy może być typem tablicy (§17.2.1). Takie tablice tablic różnią się od tablic wielowymiarowych i mogą służyć do reprezentowania "tablic postrzępionych".
Przykład:
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
przykład końcowy
Każdy typ tablicy jest typem odwołania (§8.2). Typ elementu tablicy może być dowolnym typem, w tym typami wartości i typami tablic.
17.2 Typy tablic
17.2.1 Ogólne
Produkcje gramatyczne dla typów tablic są udostępniane w §8.2.1.
Typ tablicy jest zapisywany jako non_array_type , po którym następuje co najmniej jeden rank_specifiers.
Non_array_type to dowolny typ, który nie jest sam array_type.
Ranga typu tablicy jest podawana przez najbardziej rank_specifier po lewej stronie w array_type: rank_specifier wskazuje, że tablica jest tablicą z jedną i liczbą tokenów ",
" w rank_specifier.
Typ elementu typu tablicy to typ, który wynika z usunięcia rank_specifier po lewej stronie:
- Typ tablicy formularza
T[R]
to tablica o klasyfikacjiR
i typie elementu innego niżT
tablica . - Typ tablicy formularza
T[R][R₁]...[Rₓ]
to tablica o klasyfikacjiR
i typieT[R₁]...[Rₓ]
elementu .
W efekcie rank_specifiersą odczytywane od lewej do prawej przed ostatnim typem elementu innego niż tablica.
Przykład: typ in
T[][,,][,]
to jednowymiarowa tablica trójwymiarowych tablic dwuwymiarowychint
tablic . przykład końcowy
W czasie wykonywania wartość typu tablicy może być null
lub odwołaniem do wystąpienia tego typu tablicy.
Uwaga: Zgodnie z regułami §17.6 wartość może być również odwołaniem do typu tablicy kowariantnej. notatka końcowa
17.2.2 Typ System.Array
Typ System.Array
jest abstrakcyjnym typem podstawowym wszystkich typów tablic. Niejawna konwersja odwołania (§10.2.8) istnieje z dowolnego typu tablicy do System.Array
i do dowolnego typu interfejsu zaimplementowanego przez System.Array
program . Istnieje jawna konwersja odwołania (§10.3.5) i System.Array
dowolny typ interfejsu implementowany przez System.Array
dowolny typ tablicy. System.Array
nie jest samym array_type. Jest to raczej class_type , z którego pochodzą wszystkie array_type.
W czasie wykonywania wartość typu System.Array
może być null
lub odwołaniem do wystąpienia dowolnego typu tablicy.
17.2.3 Tablice i interfejsy kolekcji ogólnej
Tablica T[]
jednowymiarowa implementuje interfejs System.Collections.Generic.IList<T>
(IList<T>
krótki) i jego podstawowe interfejsy. W związku z tym istnieje niejawna konwersja z T[]
do IList<T>
i jego interfejsów podstawowych. Ponadto, jeśli istnieje niejawna konwersja odwołania z S
do T
S[]
, następnie implementuje IList<T>
i istnieje niejawna konwersja odwołania z S[]
do IList<T>
i jego interfejsów podstawowych (§10.2.8). Jeśli istnieje jawna konwersja odwołania z S
do T
, istnieje jawna konwersja odwołania z S[]
do IList<T>
i jego interfejsów podstawowych (§10.3.5).
Podobnie tablica T[]
jednowymiarowa implementuje również interfejs System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
krótki) i jego interfejsy podstawowe. W związku z tym istnieje niejawna konwersja z T[]
do IReadOnlyList<T>
i jego interfejsów podstawowych. Ponadto, jeśli istnieje niejawna konwersja odwołania z S
do T
S[]
, następnie implementuje IReadOnlyList<T>
i istnieje niejawna konwersja odwołania z S[]
do IReadOnlyList<T>
i jego interfejsów podstawowych (§10.2.8). Jeśli istnieje jawna konwersja odwołania z S
do T
, istnieje jawna konwersja odwołania z S[]
do IReadOnlyList<T>
i jego interfejsów podstawowych (§10.3.5).
Przykład: Na przykład:
class Test { static void Main() { string[] sa = new string[5]; object[] oa1 = new object[5]; object[] oa2 = sa; IList<string> lst1 = sa; // Ok IList<string> lst2 = oa1; // Error, cast needed IList<object> lst3 = sa; // Ok IList<object> lst4 = oa1; // Ok IList<string> lst5 = (IList<string>)oa1; // Exception IList<string> lst6 = (IList<string>)oa2; // Ok IReadOnlyList<string> lst7 = sa; // Ok IReadOnlyList<string> lst8 = oa1; // Error, cast needed IReadOnlyList<object> lst9 = sa; // Ok IReadOnlyList<object> lst10 = oa1; // Ok IReadOnlyList<string> lst11 = (IReadOnlyList<string>)oa1; // Exception IReadOnlyList<string> lst12 = (IReadOnlyList<string>)oa2; // Ok } }
Przypisanie
lst2 = oa1
generuje błąd czasu kompilacji, ponieważ konwersja zobject[]
naIList<string>
jest jawną konwersją, a nie niejawną. Rzutowanie(IList<string>)oa1
spowoduje zgłoszenie wyjątku w czasie wykonywania, ponieważoa1
odwołujeobject[]
się do elementu , astring[]
nie . Jednak rzutowanie (IList<string>)oa2
nie spowoduje zgłoszenia wyjątku, ponieważoa2
odwołuje się do elementustring[]
.przykład końcowy
Zawsze, gdy istnieje niejawna lub jawna konwersja odwołania z S[]
do , istnieje również jawna konwersja odwołań z IList<T>
i jego interfejsów podstawowych do S[]
(§10.3.5IList<T>
).
Gdy typ S[]
tablicy implementuje IList<T>
element , niektóre elementy członkowskie zaimplementowanego interfejsu mogą zgłaszać wyjątki. Dokładne zachowanie implementacji interfejsu wykracza poza zakres tej specyfikacji.
17.3 Tworzenie tablicy
Wystąpienia tablic są tworzone przez array_creation_expression s (§12.8.16.5) lub przez deklaracje pól lub zmiennych lokalnych, które zawierają array_initializer (§17.7). Wystąpienia tablic można również utworzyć niejawnie w ramach oceny listy argumentów obejmującej tablicę parametrów (§15.6.2.4).
Po utworzeniu wystąpienia tablicy ranga i długość każdego wymiaru są ustanawiane, a następnie pozostają stałe przez cały okres istnienia wystąpienia. Innymi słowy, nie można zmienić rangi istniejącego wystąpienia tablicy ani zmienić rozmiaru jego wymiarów.
Wystąpienie tablicy jest zawsze typu tablicy. Typ System.Array
jest typem abstrakcyjnym, którego nie można utworzyć wystąpienia.
Elementy tablic utworzonych przez array_creation_expressions są zawsze inicjowane do ich wartości domyślnej (§9.3).
17.4 Dostęp do elementu tablicy
Dostęp do elementów tablicy uzyskuje się przy użyciu wyrażeń element_access (§12.8.11.2) formularza A[I₁, I₂, ..., Iₓ]
, gdzie A
jest wyrażeniem typu tablicy, a każdy Iₑ
z nich jest wyrażeniem typu int
, uint
, long
ulong
lub może zostać niejawnie przekonwertowany na co najmniej jeden z tych typów. Wynikiem dostępu do elementu tablicy jest zmienna, czyli element tablicy wybrany przez indeksy.
Elementy tablicy można wyliczyć przy użyciu foreach
instrukcji (§13.9.5).
17.5 Elementy członkowskie tablicy
Każdy typ tablicy dziedziczy elementy członkowskie zadeklarowane przez System.Array
typ.
Kowariancja tablicy 17.6
W przypadku dwóch reference_type s i , jeśli niejawna konwersja odwołania (§10.2.8) lub jawna konwersja odwołań (§10.3.5) istnieje od A
do B
, ta sama konwersja odwołania istnieje również z typu tablicy do typu B[R]
A[R]
tablicy , gdzie R
jest dana rank_specifier (ale taka sama dla obu typów tablic).B
A
Ta relacja jest nazywana kowariancją tablicy. Wariancja tablicy, w szczególności, oznacza, że wartość typu A[R]
tablicy może być odwołaniem do wystąpienia typu B[R]
tablicy , pod warunkiem, że niejawna konwersja odwołania istnieje z B
do A
.
Ze względu na kowariancję tablic, przypisania do elementów tablic typów odwołań obejmują sprawdzanie czasu wykonywania, które gwarantuje, że wartość przypisana do elementu tablicy jest rzeczywiście dozwolonym typem (§12.21.2).
Przykład:
class Test { static void Fill(object[] array, int index, int count, object value) { for (int i = index; i < index + count; i++) { array[i] = value; } } static void Main() { string[] strings = new string[100]; Fill(strings, 0, 100, "Undefined"); Fill(strings, 0, 10, null); Fill(strings, 90, 10, 0); } }
Przypisanie metody
array[i]
w metodzieFill
niejawnie obejmuje sprawdzanie czasu wykonywania, co gwarantuje, żevalue
jestnull
odwołaniem lub odwołaniem do obiektu typu zgodnego z rzeczywistym typemarray
elementu . WMain
systemie pierwsze dwa wywołania powodzeniaFill
, ale trzecie wywołanie powodujeSystem.ArrayTypeMismatchException
, że wykonanie pierwszego przypisania doarray[i]
metody . Wyjątek występuje, ponieważ pole nieint
może być przechowywane w tablicystring
.przykład końcowy
Kowariancja tablicy nie rozszerza się w szczególności na tablice value_types. Na przykład nie istnieje konwersja, która zezwala int[]
na traktowanie elementu jako object[]
.
Inicjatory tablicy 17.7
Inicjatory tablic można określić w deklaracjach pól (§15.5), deklaracjach zmiennych lokalnych (§13.6.2) i wyrażeniach tworzenia tablicy (§12.8.16.5):
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
Inicjator tablicy składa się z sekwencji inicjatorów zmiennych, ujętej w tokeny "{
" i "}
" i oddzielonych tokenami ",
". Każdy inicjator zmiennych jest wyrażeniem lub, w przypadku tablicy wielowymiarowej, inicjatorem zagnieżdżonej tablicy.
Kontekst, w którym jest używany inicjator tablicy, określa typ inicjowanej tablicy. W wyrażeniu tworzenia tablicy typ tablicy bezpośrednio poprzedza inicjator lub jest wnioskowany z wyrażeń w inicjatorze tablicy. W deklaracji pola lub zmiennej typ tablicy jest typem zadeklarowanego pola lub zmiennej. Gdy inicjator tablicy jest używany w deklaracji pola lub zmiennej,
int[] a = {0, 2, 4, 6, 8};
Jest to po prostu skrót dla równoważnego wyrażenia tworzenia tablicy:
int[] a = new int[] {0, 2, 4, 6, 8};
W przypadku tablicy jednowymiarowej inicjator tablicy składa się z sekwencji wyrażeń, z których każda ma niejawną konwersję na typ elementu tablicy (§10.2). Wyrażenia inicjują elementy tablicy w kolejności rosnącej, zaczynając od elementu na poziomie indeksu zero. Liczba wyrażeń w inicjatorze tablicy określa długość tworzonego wystąpienia tablicy.
Przykład: Powyższy inicjator tablicy tworzy
int[]
wystąpienie o długości 5, a następnie inicjuje wystąpienie z następującymi wartościami:a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
przykład końcowy
W przypadku tablicy wielowymiarowej inicjator tablicy musi mieć tyle poziomów zagnieżdżania, jak w tablicy znajdują się wymiary. Najbardziej oddalony poziom zagnieżdżania odpowiada poziomowi najdalej po lewej stronie, a najbardziej wewnętrzny poziom zagnieżdżania odpowiada wymiarowi z prawej strony. Długość każdego wymiaru tablicy jest określana przez liczbę elementów na odpowiednim poziomie zagnieżdżania w inicjatorze tablicy. Dla każdego inicjatora zagnieżdżonej tablicy liczba elementów jest taka sama jak inne inicjatory tablicy na tym samym poziomie.
Przykład: przykład:
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
Tworzy tablicę dwuwymiarową o długości pięciu dla wymiaru najdalej po lewej stronie i długość dwóch dla najbardziej prawego wymiaru:
int[,] b = new int[5, 2];
a następnie inicjuje wystąpienie tablicy z następującymi wartościami:
b[0, 0] = 0; b[0, 1] = 1; b[1, 0] = 2; b[1, 1] = 3; b[2, 0] = 4; b[2, 1] = 5; b[3, 0] = 6; b[3, 1] = 7; b[4, 0] = 8; b[4, 1] = 9;
przykład końcowy
Jeśli wymiar inny niż po prawej stronie ma wartość zero długości, przyjmuje się, że kolejne wymiary mają również zero długości.
Przykład:
int[,] c = {};
Tworzy dwuwymiarową tablicę o długości zero zarówno dla wymiaru lewego, jak i najbardziej prawego:
int[,] c = new int[0, 0];
przykład końcowy
Gdy wyrażenie tworzenia tablicy zawiera zarówno jawne długości wymiarów, jak i inicjator tablicy, długości muszą być wyrażeniami stałymi, a liczba elementów na każdym poziomie zagnieżdżania odpowiada odpowiedniej długości wymiaru.
Przykład: oto kilka przykładów:
int i = 3; int[] x = new int[3] {0, 1, 2}; // OK int[] y = new int[i] {0, 1, 2}; // Error, i not a constant int[] z = new int[3] {0, 1, 2, 3}; // Error, length/initializer mismatch
W tym miejscu inicjator dla
y
wyników błędu czasu kompilacji, ponieważ wyrażenie długości wymiaru nie jest stałe, a inicjator wynikówz
w czasie kompilacji błędu, ponieważ długość i liczba elementów w inicjatorze nie zgadzają się.przykład końcowy
Uwaga: język C# umożliwia przecinek końcowy na końcu array_initializer. Ta składnia zapewnia elastyczność dodawania lub usuwania elementów członkowskich z takiej listy i upraszcza generowanie maszyn takich list. notatka końcowa
ECMA C# draft specification