17 Arrays
17.1 Allgemein
Ein Array ist eine Datenstruktur, die eine Anzahl von Variablen enthält, auf die über berechnete Indizes zugegriffen wird. Die im Array enthaltenen Variablen, auch Elemente des Arrays genannt, weisen alle denselben Typ auf. Dieser Typ wird als Elementtyp des Arrays bezeichnet.
Ein Array verfügt über einen Rang, der die Anzahl der Indizes bestimmt, die jedem Arrayelement zugeordnet sind. Der Rang eines Arrays wird auch als Dimensionen des Arrays bezeichnet. Ein Array mit einer Rangfolge wird als eindimensionales Array bezeichnet. Ein Array mit einem Rang größer als einer wird als mehrdimensionales Array bezeichnet. Spezifische mehrdimensionale Arrays werden häufig als zweidimensionale Arrays, dreidimensionale Arrays usw. bezeichnet. Jede Dimension eines Arrays weist eine zugeordnete Länge auf, die eine integrale Zahl größer oder gleich Null ist. Die Dimensionslängen sind nicht Teil des Arraytyps, sondern werden eingerichtet, wenn zur Laufzeit eine Instanz des Arraytyps erstellt wird. Die Länge einer Dimension bestimmt den gültigen Indizesbereich für diese Dimension: Für eine Dimension der Länge N
können Indizes von 0
einschließlich reichen N – 1
. Die Gesamtanzahl der Elemente in einem Array ist das Produkt der Längen jeder Dimension im Array. Wenn eine oder mehrere Dimensionen eines Arrays eine Länge von Null aufweisen, wird das Array als leer angegeben.
Der Elementtyp eines Arrays kann selbst ein Arraytyp sein (§17.2.1). Solche Arrays von Arrays unterscheiden sich von mehrdimensionalen Arrays und können verwendet werden, um "gezackte Arrays" darzustellen.
Beispiel:
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
Endbeispiel
Jeder Arraytyp ist ein Bezugstyp (§8.2). Der Elementtyp eines Arrays kann ein beliebiger Typ sein, einschließlich Werttypen und Arraytypen.
17.2 Arraytypen
17.2.1 Allgemein
Die Grammatikproduktionen für Arraytypen werden in §8.2.1 bereitgestellt.
Ein Arraytyp wird als non_array_type gefolgt von mindestens einem rank_specifiergeschrieben.
Ein non_array_type ist jeder Typ , der nicht selbst ein array_type ist.
Der Rang eines Arraytyps wird von der äußerst linken rank_specifier im array_type angegeben: Ein rank_specifier gibt an, dass es sich bei dem Array um ein Array mit einer Rangfolge und der Anzahl der ",
"-Token im rank_specifier handelt.
Der Elementtyp eines Arraytyps ist der Typ, der aus dem Löschen der äußersten linken rank_specifier resultiert:
- Ein Arraytyp des Formulars
T[R]
ist ein Array mit RangR
und einem Nicht-Array-ElementtypT
. - Ein Arraytyp des Formulars
T[R][R₁]...[Rₓ]
ist ein Array mit RangR
und ElementtypT[R₁]...[Rₓ]
.
Tatsächlich werden die rank_specifiervon links nach rechts vor dem endgültigen Nicht-Array-Elementtyp gelesen.
Beispiel: Der Typ in
T[][,,][,]
ist ein eindimensionales Array von dreidimensionalen Arrays von zweidimensionalen Arrays vonint
. Endbeispiel
Zur Laufzeit kann ein Wert eines Arraytyps oder ein Verweis auf eine Instanz dieses Arraytyps sein null
.
Hinweis: Nach den Regeln von §17.6 kann der Wert auch ein Verweis auf einen kovarianten Arraytyp sein. Endnote
17.2.2 Der System.Array-Typ
Der Typ System.Array
ist der abstrakte Basistyp aller Arraytypen. Eine implizite Verweiskonvertierung (§10.2.8) ist von jedem Arraytyp zu System.Array
und zu jedem von ihnen implementierten Schnittstellentyp vorhanden System.Array
. Es ist eine explizite Verweiskonvertierung (§10.3.5) vorhanden System.Array
und alle Schnittstellentypen, die von System.Array
einem beliebigen Arraytyp implementiert werden. System.Array
ist nicht selbst ein array_type. Vielmehr handelt es sich um eine class_type , von der alle array_typeabgeleitet werden.
Zur Laufzeit kann ein Wert des Typs System.Array
oder ein Verweis auf eine Instanz eines beliebigen Arraytyps sein null
.
17.2.3 Arrays und die generischen Sammlungsschnittstellen
Ein eindimensionales Array T[]
implementiert die Schnittstelle System.Collections.Generic.IList<T>
(IList<T>
kurz) und die zugehörigen Basisschnittstellen. Dementsprechend gibt es eine implizite Konvertierung von T[]
zu IList<T>
und deren Basisschnittstellen. Darüber hinaus wird eine implizite Verweiskonvertierung von S
zu T
deren S[]
Implementierung implementiert, und es gibt eine implizite Verweiskonvertierung von S[]
und IList<T>
deren Basisschnittstellen (§10.2.8).IList<T>
Wenn es eine explizite Verweiskonvertierung von S
in T
die gibt, gibt es eine explizite Verweiskonvertierung von S[]
zu IList<T>
und deren Basisschnittstellen (§10.3.5).
Ebenso implementiert ein eindimensionales Array T[]
auch die Schnittstelle System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
kurz) und ihre Basisschnittstellen. Dementsprechend gibt es eine implizite Konvertierung von T[]
zu IReadOnlyList<T>
und deren Basisschnittstellen. Darüber hinaus wird eine implizite Verweiskonvertierung von S
zu T
deren S[]
Implementierung implementiert, und es gibt eine implizite Verweiskonvertierung von S[]
und IReadOnlyList<T>
deren Basisschnittstellen (§10.2.8).IReadOnlyList<T>
Wenn es eine explizite Verweiskonvertierung von S
in T
die gibt, gibt es eine explizite Verweiskonvertierung von S[]
zu IReadOnlyList<T>
und deren Basisschnittstellen (§10.3.5).
Beispiel:
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 } }
Die Zuordnung
lst2 = oa1
generiert einen Kompilierungsfehler, da die Konvertierung vonobject[]
zuIList<string>
einer expliziten Konvertierung und nicht implizit ist. Die Umwandlung(IList<string>)oa1
führt dazu, dass eine Ausnahme zur Laufzeit ausgelöst wird, daoa1
verweise auf einobject[]
und nicht einstring[]
. Die Umwandlung (IList<string>)oa2
führt jedoch nicht dazu, dass eine Ausnahme ausgelöst wird, daoa2
verweise auf einestring[]
.Endbeispiel
Wenn es eine implizite oder explizite Referenzkonvertierung von S[]
zu IList<T>
, gibt es auch eine explizite Verweiskonvertierung von IList<T>
und deren Basisschnittstellen in S[]
(§10.3.5).
Wenn ein Arraytyp S[]
implementiert wird IList<T>
, können einige Der Member der implementierten Schnittstelle Ausnahmen auslösen. Das genaue Verhalten der Implementierung der Schnittstelle liegt außerhalb des Umfangs dieser Spezifikation.
17.3 Arrayerstellung
Arrayinstanzen werden von array_creation_expression s (§12.8.16.5) oder von Feld- oder lokalen Variablendeklarationen erstellt, die eine array_initializer (§17.7) enthalten. Arrayinstanzen können auch implizit als Teil der Auswertung einer Argumentliste mit einem Parameterarray (§15.6.2.4) erstellt werden.
Wenn eine Arrayinstanz erstellt wird, werden der Rang und die Länge jeder Dimension eingerichtet und bleiben dann für die gesamte Lebensdauer der Instanz konstant. Mit anderen Worten, es ist nicht möglich, den Rang einer vorhandenen Arrayinstanz zu ändern, noch ist es möglich, die Größe seiner Dimensionen zu ändern.
Eine Arrayinstanz weist immer einen Arraytyp auf. Der System.Array
Typ ist ein abstrakter Typ, der nicht instanziiert werden kann.
Elemente von Arrays, die von array_creation_expressions erstellt werden, werden immer mit ihrem Standardwert initialisiert (§9.3).
17.4 Arrayelementzugriff
Auf Arrayelemente wird mithilfe von element_access Ausdrücken (§12.8.11.2) des Formulars A[I₁, I₂, ..., Iₓ]
zugegriffen, wobei A
es sich um einen Ausdruck eines Arraytyps handelt, wobei es sich um Iₑ
einen Ausdruck vom Typ int
, , uint
, long
, ulong
oder die implizite Konvertierung in einen oder mehrere dieser Typen handelt. Das Ergebnis eines Arrayelementzugriffs ist eine Variable, nämlich das Arrayelement, das von den Indizes ausgewählt wurde.
Die Elemente eines Arrays können mithilfe einer foreach
Anweisung (§13.9.5) aufgezählt werden.
17.5 Arraymitglieder
Jeder Arraytyp erbt die vom System.Array
Typ deklarierten Member.
17.6 Arraykovarianz
Bei zwei reference_type A
und B
, wenn eine implizite Verweiskonvertierung (§10.2.8) oder explizite Verweiskonvertierung (§10.3.5) von A
in B
, ist die gleiche Verweiskonvertierung auch von dem Arraytyp in den Arraytyp B[R]
A[R]
vorhanden, wobei R
ein beliebiger rank_specifier (aber für beide Arraytypen identisch) ist. Diese Beziehung wird als Arraykovarianz bezeichnet. Die Arraykovarianz bedeutet insbesondere, dass ein Wert eines Arraytyps A[R]
tatsächlich ein Verweis auf eine Instanz eines Arraytyps B[R]
sein kann, vorausgesetzt, eine implizite Verweiskonvertierung ist von B
zu A
.
Aufgrund der Arraykovarianz enthalten Zuordnungen zu Elementen des Referenztyparrays eine Laufzeitüberprüfung, mit der sichergestellt wird, dass der dem Arrayelement zugewiesene Wert tatsächlich einen zulässigen Typ aufweist (§12.21.2).
Beispiel:
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); } }
Die Zuordnung
array[i]
in derFill
Methode enthält implizit eine Laufzeitüberprüfung, die sicherstellt, dass esvalue
sich um einennull
Verweis oder einen Verweis auf ein Objekt eines Typs handelt, der mit dem tatsächlichen Elementtyparray
kompatibel ist. InMain
, die ersten beiden Aufrufe erfolgreichFill
, aber der dritte Aufruf bewirkt, dass eineSystem.ArrayTypeMismatchException
ausgelöst wird, wenn die erste Zuordnung ausgeführtarray[i]
wird. Die Ausnahme tritt auf, da ein Boxedint
nicht in einemstring
Array gespeichert werden kann.Endbeispiel
Arraykovarianz erstreckt sich speziell nicht auf Arrays von value_types. Beispielsweise ist keine Konvertierung vorhanden, die eine int[]
Behandlung als eine object[]
.
17.7 Arrayinitialisierer
Arrayinitialisierer können in Felddeklarationen (§15.5), lokalen Variablendeklarationen (§13.6.2) und Arrayerstellungsausdrücken (§12.8.16.5) angegeben werden:
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
Ein Arrayinitialisierer besteht aus einer Sequenz variabler Initialisierer, eingeschlossen durch "{
" und "}
" Token und durch "" Token getrennt durch ",
"-Token. Jeder Variableninitialisierer ist ein Ausdruck oder im Fall eines mehrdimensionalen Arrays ein geschachtelter Arrayinitialisierer.
Der Kontext, in dem ein Arrayinitialisierer verwendet wird, bestimmt den Typ des Arrays, das initialisiert wird. In einem Arrayerstellungsausdruck steht der Arraytyp unmittelbar vor dem Initialisierer oder wird von den Ausdrücken im Arrayinitialisierer abgeleitet. In einer Feld- oder Variablendeklaration ist der Arraytyp der Typ des deklarierten Felds oder der Variablen. Wenn ein Arrayinitialisierer in einer Feld- oder Variablendeklaration verwendet wird,
int[] a = {0, 2, 4, 6, 8};
es ist einfach kurz für einen entsprechenden Arrayerstellungsausdruck:
int[] a = new int[] {0, 2, 4, 6, 8};
Bei einem eindimensionalen Array muss der Arrayinitialisierer aus einer Abfolge von Ausdrücken bestehen, die jeweils eine implizite Konvertierung in den Elementtyp des Arrays haben (§10.2). Die Ausdrücke initialisieren Arrayelemente in zunehmender Reihenfolge, beginnend mit dem Element bei Index 0. Die Anzahl der Ausdrücke im Arrayinitialisierer bestimmt die Länge der arrayinstanz, die erstellt wird.
Beispiel: Der obige Arrayinitialisierer erstellt eine
int[]
Instanz der Länge 5 und initialisiert dann die Instanz mit den folgenden Werten:a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
Endbeispiel
Bei einem mehrdimensionalen Array muss der Arrayinitialisierer so viele Schachtelungsebenen aufweisen, wie dimensionen im Array vorhanden sind. Die äußerste Schachtelungsebene entspricht der äußersten Dimension ganz links, und die innerste Schachtelungsebene entspricht der ganz rechts stehenden Dimension. Die Länge jeder Dimension des Arrays wird durch die Anzahl der Elemente auf der entsprechenden Schachtelungsebene im Arrayinitialisierer bestimmt. Für jeden geschachtelten Arrayinitialisierer muss die Anzahl der Elemente mit den anderen Arrayinitialisierern auf derselben Ebene identisch sein.
Beispiel: Das Beispiel:
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
erstellt ein zweidimensionales Array mit einer Länge von fünf für die äußerst linke Dimension und eine Länge von zwei für die äußerst rechte Dimension:
int[,] b = new int[5, 2];
und initialisiert dann die Arrayinstanz mit den folgenden Werten:
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;
Endbeispiel
Wenn eine andere Dimension als die äußerst rechte Dimension mit der Länge Null angegeben wird, wird davon ausgegangen, dass die nachfolgenden Dimensionen ebenfalls die Länge Null aufweisen.
Beispiel:
int[,] c = {};
erstellt ein zweidimensionales Array mit einer Länge von Null sowohl für die äußerste linke als auch für die äußerst rechte Dimension:
int[,] c = new int[0, 0];
Endbeispiel
Wenn ein Arrayerstellungsausdruck sowohl explizite Dimensionslängen als auch ein Arrayinitialisierer enthält, müssen die Längen konstanten Ausdrücke sein, und die Anzahl der Elemente auf jeder Schachtelungsebene entspricht der entsprechenden Dimensionslänge.
Beispiel: Hier sind einige Beispiele:
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
Hier führt der Initialisierer zu
y
einem Kompilierungszeitfehler, da der Bemaßungslängenausdruck keine Konstante ist, und der Initialisierer fürz
ergebnisse in einem Kompilierungszeitfehler, da die Länge und die Anzahl der Elemente im Initialisierer nicht übereinstimmen.Endbeispiel
Hinweis: C# ermöglicht ein nachfolgendes Komma am Ende eines array_initializer. Diese Syntax bietet Flexibilität beim Hinzufügen oder Löschen von Mitgliedern aus einer solchen Liste und vereinfacht die Computergenerierung solcher Listen. Endnote
ECMA C# draft specification