Partager via


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 rang R et un type Td’élément non matricielle.
  • Un type de tableau du formulaire T[R][R₁]...[Rₓ] est un tableau avec un rang R et un type T[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 de int. 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 en object[] 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, car oa1 elle fait référence à un object[] et non à un string[]. 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 à un string[].

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, ulongou 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 à Bpartir 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).BA 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écution Fill , ce qui garantit qu’il value s’agit d’une null référence ou d’une référence à un objet d’un type compatible avec le type d’élément réel de array. Dans Main, les deux premiers appels de réussite, mais la troisième invocation provoque une System.ArrayTypeMismatchException levée lors de Fill l’exécution de la première affectation à array[i]. L’exception se produit car un boxed int ne peut pas être stocké dans un string 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 pour z 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