Udostępnij za pośrednictwem


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 Nindeksy 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 klasyfikacji R i typie elementu innego niż Ttablica .
  • Typ tablicy formularza T[R][R₁]...[Rₓ] to tablica o klasyfikacji R i typie T[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 dwuwymiarowych inttablic . 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.Arrayprogram . 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 z object[] na IList<string> jest jawną konwersją, a nie niejawną. Rzutowanie (IList<string>)oa1 spowoduje zgłoszenie wyjątku w czasie wykonywania, ponieważ oa1 odwołuje object[] się do elementu , a string[]nie . Jednak rzutowanie (IList<string>)oa2 nie spowoduje zgłoszenia wyjątku, ponieważ oa2 odwołuje się do elementu string[].

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, longulonglub 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).BA 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 metodzie Fill niejawnie obejmuje sprawdzanie czasu wykonywania, co gwarantuje, że value jest null odwołaniem lub odwołaniem do obiektu typu zgodnego z rzeczywistym typem arrayelementu . W Mainsystemie pierwsze dwa wywołania powodzenia Fill , ale trzecie wywołanie powoduje System.ArrayTypeMismatchException , że wykonanie pierwszego przypisania do array[i]metody . Wyjątek występuje, ponieważ pole nie int może być przechowywane w tablicy string .

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ów z 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