17 Tableaux
17.1 Général
Un tableau est une structure de données qui contient un certain nombre de variables qui sont accessibles par des indices calculés. Les variables contenues dans un tableau, également appelé éléments du tableau, sont tous du même type, et ce type est appelé type d’élément du tableau.
Un tableau a un rang qui détermine le nombre d’index associés à chaque élément de tableau. Le rang d’un tableau est également appelé dimensions du tableau. Un tableau avec un rang d’un est appelé tableau unidimensionnel. Un tableau avec un rang supérieur à un est appelé tableau multidimensionnel. Des tableaux multidimensionnels de taille spécifique sont souvent appelés tableaux à deux dimensions, tableaux tridimensionnels, et ainsi de suite. Chaque dimension d’un tableau a une longueur associée qui est un nombre intégral supérieur ou égal à zéro. Les longueurs de dimension ne font pas partie du type du tableau, mais sont établies lorsqu’une instance du type de tableau est créée au moment de l’exécution. La longueur d’une dimension détermine la plage valide d’index pour cette dimension : pour une dimension de longueur N
, les index peuvent aller de 0
à N – 1
l’inclusion. Le nombre total d’éléments d’un tableau est le produit des longueurs de chaque dimension du tableau. Si une ou plusieurs dimensions d’un tableau ont une longueur de zéro, le tableau est dit vide.
Le type d’élément d’un tableau peut lui-même être un type de tableau (§17.2.1). Ces tableaux de tableaux sont distincts des tableaux multidimensionnels et peuvent être utilisés pour représenter des « tableaux en jaquet ».
Exemple :
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
exemple de fin
Chaque type de tableau est un type de référence (§8.2). Le type d’élément d’un tableau peut être n’importe quel type, y compris les types valeur et les types de tableau.
17.2 Types de tableaux
17.2.1 Général
Les productions grammaticales pour les types de tableaux sont fournies dans le §8.2.1.
Un type de tableau est écrit en tant que non_array_type suivi d’une ou plusieurs rank_specifiers.
Un non_array_type est tout type qui n’est pas lui-même un array_type.
Le rang d’un type de tableau est donné par le rank_specifier le plus à gauche dans l’array_type : un rank_specifier indique que le tableau est un tableau avec un rang d’un plus le nombre de jetons «,
» dans le rank_specifier.
Le type d’élément d’un type de tableau est le type qui résulte de la suppression de la rank_specifier la plus à gauche :
- Un type de tableau du formulaire
T[R]
est un tableau avec un rangR
et un typeT
d’élément non matricielle. - Un type de tableau du formulaire
T[R][R₁]...[Rₓ]
est un tableau avec un rangR
et un typeT[R₁]...[Rₓ]
d’élément.
En effet, les rank_specifiersont lus de gauche à droite avant le type final d’élément non matricielle.
Exemple : le type dans
T[][,,][,]
est un tableau unidimensionnel de tableaux tridimensionnels de tableaux à deux dimensions deint
. exemple de fin
Au moment de l’exécution, une valeur d’un type de tableau peut être null
ou une référence à une instance de ce type de tableau.
Remarque : en suivant les règles de §17.6, la valeur peut également être une référence à un type de tableau covariant. Note de fin
17.2.2 Type System.Array
Le type System.Array
est le type de base abstrait de tous les types de tableaux. Une conversion de référence implicite (§10.2.8) existe de n’importe quel type de tableau vers et vers System.Array
n’importe quel type d’interface implémenté par System.Array
. Une conversion de référence explicite (§10.3.5) existe depuis System.Array
et n’importe quel type d’interface implémenté par System.Array
n’importe quel type de tableau. System.Array
n’est pas lui-même un array_type. Il s’agit plutôt d’une class_type à partir de laquelle tous les array_typesont dérivés.
Au moment de l’exécution, une valeur de type System.Array
peut être null
ou une référence à une instance de n’importe quel type de tableau.
17.2.3 Tableaux et interfaces de collection générique
Un tableau T[]
unidimensionnel implémente l’interface System.Collections.Generic.IList<T>
(IList<T>
pour un court) et ses interfaces de base. En conséquence, il existe une conversion implicite de T[]
vers IList<T>
et de ses interfaces de base. En outre, s’il existe une conversion de référence implicite à partir de S
laquelle T
S[]
il implémente IList<T>
et qu’il existe une conversion de référence implicite depuis et ses interfaces de IList<T>
S[]
base (§10.2.8). S’il existe une conversion de référence explicite à partir de S
laquelle T
il existe une conversion de référence explicite depuis IList<T>
et ses interfaces de S[]
base (§10.3.5).
De même, un tableau T[]
unidimensionnel implémente également l’interface System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
pour un court) et ses interfaces de base. En conséquence, il existe une conversion implicite de T[]
vers IReadOnlyList<T>
et de ses interfaces de base. En outre, s’il existe une conversion de référence implicite à partir de S
laquelle T
S[]
il implémente IReadOnlyList<T>
et qu’il existe une conversion de référence implicite depuis et ses interfaces de IReadOnlyList<T>
S[]
base (§10.2.8). S’il existe une conversion de référence explicite à partir de S
laquelle T
il existe une conversion de référence explicite depuis IReadOnlyList<T>
et ses interfaces de S[]
base (§10.3.5).
Exemple : Par exemple :
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 } }
L’affectation
lst2 = oa1
génère une erreur au moment de la compilation, car la conversion enobject[]
IList<string>
est une conversion explicite, et non implicite. Le cast(IList<string>)oa1
entraîne la levée d’une exception au moment de l’exécution, caroa1
elle fait référence à unobject[]
et non à unstring[]
. Toutefois, le cast (IList<string>)oa2
n’entraîne pas la levée d’une exception dans la mesure oùoa2
fait référence à unstring[]
.exemple de fin
Chaque fois qu’il existe une conversion de référence implicite ou explicite depuis S[]
IList<T>
, il existe également une conversion de référence explicite depuis et ses interfaces de IList<T>
base vers S[]
(§10.3.5).
Lorsqu’un type S[]
de tableau implémente IList<T>
, certains membres de l’interface implémentée peuvent lever des exceptions. Le comportement précis de l’implémentation de l’interface dépasse la portée de cette spécification.
17.3 Création de tableaux
Les instances de tableau sont créées par des array_creation_expression(§12.8.16.5) ou par des déclarations de variables locales ou de champ qui incluent un array_initializer (§17.7). Les instances de tableau peuvent également être créées implicitement dans le cadre de l’évaluation d’une liste d’arguments impliquant un tableau de paramètres (§15.6.2.4).
Lorsqu’une instance de tableau est créée, le classement et la longueur de chaque dimension sont établis, puis restent constants pendant toute la durée de vie de l’instance. En d’autres termes, il n’est pas possible de modifier le rang d’une instance de tableau existante, ni de redimensionner ses dimensions.
Une instance de tableau est toujours d’un type de tableau. Le System.Array
type est un type abstrait qui ne peut pas être instancié.
Les éléments des tableaux créés par array_creation_expressions sont toujours initialisés à leur valeur par défaut (§9.3).
Accès aux éléments de tableau 17.4
Les éléments de tableau sont accessibles à l’aide d’expressions element_access (§12.8.11.2) du formulaire A[I₁, I₂, ..., Iₓ]
, où A
est une expression d’un type de tableau et chacune Iₑ
est une expression de type int
, uint
, long
, ulong
ou peut être convertie implicitement en un ou plusieurs de ces types. Le résultat d’un accès à un élément de tableau est une variable, à savoir l’élément de tableau sélectionné par les index.
Les éléments d’un tableau peuvent être énumérés à l’aide d’une foreach
instruction (§13.9.5).
17.5 Membres du tableau
Chaque type de tableau hérite des membres déclarés par le System.Array
type.
17.6 Covariance de tableau
Pour deux reference_type s et, si une conversion de référence implicite (§10.2.8) ou une conversion de référence explicite (§10.3.5) existe à B
partir A
de , la même conversion de référence existe également du type de tableau au type A[R]
B[R]
de tableau, où R
est une rank_specifier donnée (mais identique pour les deux types de tableaux).B
A
Cette relation est appelée covariance de tableau. La covariance de tableau, en particulier, signifie qu’une valeur d’un type A[R]
de tableau peut en fait être une référence à une instance d’un type B[R]
de tableau , à condition qu’une conversion de référence implicite existe depuis B
A
.
En raison de la covariance de tableau, les affectations aux éléments des tableaux de types de référence incluent une vérification au moment de l’exécution qui garantit que la valeur affectée à l’élément de tableau est en fait d’un type autorisé (§12.21.2).
Exemple :
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); } }
L’affectation à
array[i]
dans la méthode inclut implicitement une vérification au moment de l’exécutionFill
, ce qui garantit qu’ilvalue
s’agit d’unenull
référence ou d’une référence à un objet d’un type compatible avec le type d’élément réel dearray
. DansMain
, les deux premiers appels de réussite, mais la troisième invocation provoque uneSystem.ArrayTypeMismatchException
levée lors deFill
l’exécution de la première affectation àarray[i]
. L’exception se produit car un boxedint
ne peut pas être stocké dans unstring
tableau.exemple de fin
La covariance de tableau ne s’étend pas spécifiquement aux tableaux de value_types. Par exemple, aucune conversion n’existe qui permet int[]
d’être traitée comme un object[]
.
17.7 Initialiseurs de tableau
Les initialiseurs de tableau peuvent être spécifiés dans les déclarations de champ (§15.5), les déclarations de variables locales (§13.6.2) et les expressions de création de tableau (§12.8.16.5) :
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
Un initialiseur de tableau se compose d’une séquence d’initialiseurs de variables, entourés de jetons «{
» et «}
» et séparés par des jetons «,
». Chaque initialiseur de variable est une expression ou, dans le cas d’un tableau multidimensionnel, un initialiseur de tableau imbriqué.
Le contexte dans lequel un initialiseur de tableau est utilisé détermine le type du tableau initialisé. Dans une expression de création de tableau, le type de tableau précède immédiatement l’initialiseur ou est déduit des expressions dans l’initialiseur de tableau. Dans une déclaration de champ ou de variable, le type de tableau est le type du champ ou de la variable déclaré. Lorsqu’un initialiseur de tableau est utilisé dans une déclaration de champ ou de variable,
int[] a = {0, 2, 4, 6, 8};
il est simplement abrégé pour une expression de création de tableau équivalente :
int[] a = new int[] {0, 2, 4, 6, 8};
Pour un tableau unidimensionnel, l’initialiseur de tableau doit se composer d’une séquence d’expressions, chacune ayant une conversion implicite vers le type d’élément du tableau (§10.2). Les expressions initialisent les éléments de tableau dans l’ordre croissant, en commençant par l’élément à l’index zéro. Le nombre d’expressions dans l’initialiseur de tableau détermine la longueur de l’instance de tableau en cours de création.
Exemple : l’initialiseur de tableau ci-dessus crée une
int[]
instance de longueur 5, puis initialise l’instance avec les valeurs suivantes :a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
exemple de fin
Pour un tableau multidimensionnel, l’initialiseur de tableau doit avoir autant de niveaux d’imbrication qu’il existe des dimensions dans le tableau. Le niveau d’imbrication le plus externe correspond à la dimension la plus à gauche et le niveau d’imbrication le plus profond correspond à la dimension la plus à droite. La longueur de chaque dimension du tableau est déterminée par le nombre d’éléments au niveau d’imbrication correspondant dans l’initialiseur de tableau. Pour chaque initialiseur de tableau imbriqué, le nombre d’éléments doit être identique à celui des autres initialiseurs de tableau au même niveau.
Exemple : l’exemple :
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
crée un tableau à deux dimensions avec une longueur de cinq pour la dimension la plus à gauche et une longueur de deux pour la dimension la plus à droite :
int[,] b = new int[5, 2];
puis initialise l’instance de tableau avec les valeurs suivantes :
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;
exemple de fin
Si une dimension autre que la plus à droite est donnée avec la longueur zéro, les dimensions suivantes sont supposées avoir une longueur égale à zéro.
Exemple :
int[,] c = {};
crée un tableau à deux dimensions avec une longueur de zéro pour la dimension la plus à gauche et la dimension la plus à droite :
int[,] c = new int[0, 0];
exemple de fin
Lorsqu’une expression de création de tableau inclut des longueurs de dimension explicites et un initialiseur de tableau, les longueurs doivent être des expressions constantes et le nombre d’éléments à chaque niveau d’imbrication doit correspondre à la longueur de dimension correspondante.
Exemple : Voici quelques exemples :
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
Ici, l’initialiseur pour
y
résultats dans une erreur au moment de la compilation, car l’expression de longueur de dimension n’est pas une constante, et l’initialiseur pourz
résultats dans une erreur de compilation, car la longueur et le nombre d’éléments dans l’initialiseur ne sont pas d’accord.exemple de fin
Remarque : C# autorise une virgule de fin à la fin d’une array_initializer. Cette syntaxe permet d’ajouter ou de supprimer des membres d’une telle liste et simplifie la génération d’ordinateurs de ces listes. Note de fin
ECMA C# draft specification