17 matrici
17.1 Generale
Una matrice è una struttura di dati contenente una serie di variabili accessibili tramite indici calcolati. Le variabili contenute in una matrice, chiamate anche elementi della matrice, sono tutte dello stesso tipo, definito tipo di elemento della matrice.
Una matrice ha un rango che determina il numero di indici associati a ogni elemento della matrice. Il rango di una matrice viene definito anche come dimensioni della matrice. Una matrice con un rango di uno è denominata matrice unidimensionale. Una matrice con un rango maggiore di una è detta matrice multidimensionale. Matrici multidimensionali specifiche sono spesso definite matrici bidimensionali, matrici tridimensionali e così via. Ogni dimensione di una matrice ha una lunghezza associata che è un numero integrale maggiore o uguale a zero. Le lunghezze della dimensione non fanno parte del tipo della matrice, ma vengono invece stabilite quando viene creata un'istanza del tipo di matrice in fase di esecuzione. La lunghezza di una dimensione determina l'intervallo valido di indici per tale dimensione: per una dimensione di lunghezza N
, gli indici possono variare da 0
a N – 1
inclusivo. Il numero totale di elementi in una matrice è il prodotto delle lunghezze di ogni dimensione nella matrice. Se una o più dimensioni di una matrice hanno una lunghezza pari a zero, la matrice viene considerata vuota.
Il tipo di elemento di una matrice può essere un tipo di matrice (§17.2.1). Tali matrici di matrici sono distinte da matrici multidimensionali e possono essere usate per rappresentare "matrici irregolari".
Esempio:
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
esempio finale
Ogni tipo di matrice è un tipo riferimento (§8.2). Il tipo di elemento di una matrice può essere qualsiasi tipo, inclusi i tipi valore e i tipi di matrice.
17.2 Tipi di matrice
17.2.1 Generale
Le produzioni grammaticali per i tipi di matrice sono disponibili in §8.2.1.
Un tipo di matrice viene scritto come non_array_type seguito da uno o più rank_specifiers.
Un non_array_type è qualsiasi tipo che non è un array_type.
Il rango di un tipo di matrice viene assegnato dall'rank_specifier più a sinistra nella array_type: un rank_specifier indica che la matrice è una matrice con un rango di uno più il numero di token ",
" nella rank_specifier.
Il tipo di elemento di un tipo di matrice è il tipo risultante dall'eliminazione dell'rank_specifier più a sinistra:
- Un tipo di matrice del modulo
T[R]
è una matrice con classificazioneR
e un tipo diT
elemento non matrice . - Un tipo di matrice del modulo
T[R][R₁]...[Rₓ]
è una matrice con classificazioneR
e un tipo diT[R₁]...[Rₓ]
elemento .
In effetti, i rank_specifiervengono letti da sinistra a destra prima del tipo finale di elemento non matrice.
Esempio: il tipo in
T[][,,][,]
è una matrice unidimensionale di matrici tridimensionali di matrici bidimensionali diint
. esempio finale
In fase di esecuzione, un valore di un tipo di matrice può essere null
o un riferimento a un'istanza di tale tipo di matrice.
Nota: seguendo le regole di §17.6, il valore può anche essere un riferimento a un tipo di matrice covariante. nota finale
17.2.2 Il tipo System.Array
Il tipo è il tipo System.Array
di base astratto di tutti i tipi di matrice. Esiste una conversione implicita dei riferimenti (§10.2.8) da qualsiasi tipo di matrice a System.Array
e a qualsiasi tipo di interfaccia implementato da System.Array
. Esiste una conversione esplicita dei riferimenti (§10.3.5) da System.Array
e qualsiasi tipo di interfaccia implementato da System.Array
a qualsiasi tipo di matrice. System.Array
non è un array_type. Invece, si tratta di un class_type da cui derivano tutti i array_types.
In fase di esecuzione, un valore di tipo System.Array
può essere null
o un riferimento a un'istanza di qualsiasi tipo di matrice.
17.2.3 Matrici e interfacce di raccolta generiche
Una matrice T[]
unidimensionale implementa l'interfaccia System.Collections.Generic.IList<T>
(IList<T>
per brevità) e le relative interfacce di base. Di conseguenza, è presente una conversione implicita da T[]
a IList<T>
e le relative interfacce di base. Inoltre, se è presente una conversione implicita dei riferimenti da a , implementa IList<T>
e si verifica una conversione implicita dei riferimenti da S[]
a IList<T>
e le relative interfacce di base (§10.2.8).S[]
T
S
Se è presente una conversione esplicita dei riferimenti da S
a T
, è presente una conversione esplicita dei riferimenti da S[]
a IList<T>
e le relative interfacce di base (§10.3.5).
Analogamente, una matrice T[]
unidimensionale implementa anche l'interfaccia System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
per brevità) e le relative interfacce di base. Di conseguenza, è presente una conversione implicita da T[]
a IReadOnlyList<T>
e le relative interfacce di base. Inoltre, se è presente una conversione implicita dei riferimenti da a , implementa IReadOnlyList<T>
e si verifica una conversione implicita dei riferimenti da S[]
a IReadOnlyList<T>
e le relative interfacce di base (§10.2.8).S[]
T
S
Se è presente una conversione esplicita dei riferimenti da S
a T
, è presente una conversione esplicita dei riferimenti da S[]
a IReadOnlyList<T>
e le relative interfacce di base (§10.3.5).
Esempio: ad esempio:
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'assegnazione
lst2 = oa1
genera un errore in fase di compilazione perché la conversione daobject[]
aIList<string>
è una conversione esplicita, non implicita. Il cast(IList<string>)oa1
genererà un'eccezione in fase di esecuzione perchéoa1
fa riferimento a un oggettoobject[]
e non a .string[]
Tuttavia, il cast (IList<string>)oa2
non genererà un'eccezione perchéoa2
fa riferimento a un oggettostring[]
.esempio finale
Ogni volta che è presente una conversione implicita o esplicita dei riferimenti da S[]
a IList<T>
, esiste anche una conversione esplicita dei riferimenti da IList<T>
e le relative interfacce di base a S[]
(§10.3.5).
Quando un tipo di S[]
matrice implementa IList<T>
, alcuni dei membri dell'interfaccia implementata possono generare eccezioni. Il comportamento preciso dell'implementazione dell'interfaccia esula dall'ambito di questa specifica.
17.3 Creazione di matrici
Le istanze di matrice vengono create da array_creation_expression s (§12.8.16.5) o da dichiarazioni di variabili locali o di campo che includono un array_initializer (§17.7). È anche possibile creare istanze di matrice in modo implicito come parte della valutazione di un elenco di argomenti che include una matrice di parametri (§15.6.2.4).
Quando viene creata un'istanza di matrice, la classificazione e la lunghezza di ogni dimensione vengono stabilite e quindi rimangono costanti per l'intera durata dell'istanza. In altre parole, non è possibile modificare il rango di un'istanza di matrice esistente, né è possibile ridimensionarne le dimensioni.
Un'istanza di matrice è sempre di un tipo di matrice. Il System.Array
tipo è un tipo astratto di cui non è possibile creare un'istanza.
Gli elementi delle matrici create da array_creation_expressionvengono sempre inizializzati sul valore predefinito (§9.3).
17.4 Accesso all'elemento array
È possibile accedere agli elementi della matrice usando element_access espressioni (§12.8.11.2) del formato A[I₁, I₂, ..., Iₓ]
, dove A
è un'espressione di un tipo di matrice e ognuna Iₑ
è un'espressione di tipo int
, , uint
long
, ulong
o può essere convertita in modo implicito in uno o più di questi tipi. Il risultato dell'accesso a un elemento di matrice è una variabile, ovvero l'elemento della matrice selezionato dagli indici.
Gli elementi di una matrice possono essere enumerati utilizzando un'istruzione foreach
(§13.9.5).
17.5 Membri della matrice
Ogni tipo di matrice eredita i membri dichiarati dal System.Array
tipo .
17.6 Covarianza matrice
Per due reference_type e B
, se esiste una conversione implicita dei riferimenti (§10.2.8) o una conversione esplicita dei riferimenti (§10.3.5) da A
a B
, la stessa conversione dei riferimenti esiste anche dal tipo di matrice al tipo A[R]
B[R]
di matrice , dove R
è qualsiasi rank_specifier specificato (ma uguale per entrambi i tipi di matrice).A
Questa relazione è nota come covarianza della matrice. La covarianza della matrice, in particolare, indica che un valore di un tipo di A[R]
matrice potrebbe effettivamente essere un riferimento a un'istanza di un tipo di B[R]
matrice , purché esista una conversione di riferimento implicita da B
a A
.
A causa della covarianza della matrice, le assegnazioni agli elementi delle matrici di tipi di riferimento includono un controllo di runtime che assicura che il valore assegnato all'elemento della matrice sia effettivamente di un tipo consentito (§12.21.2).
Esempio:
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'assegnazione a
array[i]
nel metodo include in modo implicito un controllo di runtime, che garantisce chevalue
sia unnull
riferimento o un riferimento a un oggetto di un tipo compatibile con il tipo di elemento effettivo diarray
.Fill
InMain
le prime due chiamate diFill
hanno esito positivo, ma la terza chiamata genera un'eccezioneSystem.ArrayTypeMismatchException
durante l'esecuzione della prima assegnazione aarray[i]
. L'eccezione si verifica perché un boxed non può essere archiviatoint
in unastring
matrice.esempio finale
La covarianza della matrice non si estende in modo specifico alle matrici di value_types. Ad esempio, non esiste alcuna conversione che consente a un oggetto int[]
di essere considerato come .object[]
Inizializzatori di matrice 17.7
Gli inizializzatori di matrice possono essere specificati nelle dichiarazioni di campo (§15.5), nelle dichiarazioni di variabili locali (§13.6.2) e nelle espressioni di creazione della matrice (§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 inizializzatore di matrice è costituito da una sequenza di inizializzatori di variabili, racchiusi tra token "{
" e "}
" e separati da token ",
". Ogni inizializzatore di variabile è un'espressione o, nel caso di una matrice multidimensionale, un inizializzatore di matrice annidato.
Il contesto in cui viene utilizzato un inizializzatore di matrice determina il tipo della matrice da inizializzare. In un'espressione di creazione della matrice, il tipo di matrice precede immediatamente l'inizializzatore o viene dedotto dalle espressioni nell'inizializzatore di matrice. In una dichiarazione di campo o variabile, il tipo di matrice è il tipo del campo o della variabile dichiarata. Quando un inizializzatore di matrice viene usato in una dichiarazione di campo o variabile,
int[] a = {0, 2, 4, 6, 8};
è semplicemente abbreviato per un'espressione di creazione di matrice equivalente:
int[] a = new int[] {0, 2, 4, 6, 8};
Per una matrice unidimensionale, l'inizializzatore di matrice deve essere costituito da una sequenza di espressioni, ognuna con una conversione implicita nel tipo di elemento della matrice (§10.2). Le espressioni inizializzano gli elementi della matrice in ordine crescente, a partire dall'elemento in corrispondenza dell'indice zero. Il numero di espressioni nell'inizializzatore di matrice determina la lunghezza dell'istanza della matrice da creare.
Esempio: l'inizializzatore di matrice precedente crea un'istanza
int[]
di lunghezza 5 e quindi inizializza l'istanza con i valori seguenti:a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
esempio finale
Per una matrice multidimensionale, l'inizializzatore di matrice deve avere tutti i livelli di annidamento presenti nella matrice. Il livello di annidamento più esterno corrisponde alla dimensione più a sinistra e il livello di annidamento più interno corrisponde alla dimensione più a destra. La lunghezza di ogni dimensione della matrice è determinata dal numero di elementi al livello di annidamento corrispondente nell'inizializzatore di matrice. Per ogni inizializzatore di matrice annidato, il numero di elementi deve corrispondere agli altri inizializzatori di matrice allo stesso livello.
Esempio: Esempio:
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
crea una matrice bidimensionale con una lunghezza di cinque per la dimensione più a sinistra e una lunghezza di due per la dimensione più a destra:
int[,] b = new int[5, 2];
e quindi inizializza l'istanza della matrice con i valori seguenti:
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;
esempio finale
Se viene specificata una dimensione diversa dalla più a destra con lunghezza zero, si presuppone che anche le dimensioni successive abbiano lunghezza zero.
Esempio:
int[,] c = {};
crea una matrice bidimensionale con una lunghezza pari a zero sia per la dimensione più a sinistra che per la dimensione più a destra:
int[,] c = new int[0, 0];
esempio finale
Quando un'espressione di creazione di matrice include sia lunghezze di dimensione esplicite che un inizializzatore di matrice, le lunghezze devono essere espressioni costanti e il numero di elementi a ogni livello di annidamento deve corrispondere alla lunghezza della dimensione corrispondente.
Esempio: ecco alcuni esempi:
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
In questo caso, l'inizializzatore per
y
genera un errore in fase di compilazione perché l'espressione di lunghezza della dimensione non è una costante e l'inizializzatore perz
genera un errore in fase di compilazione perché la lunghezza e il numero di elementi nell'inizializzatore non sono d'accordo.esempio finale
Nota: C# consente una virgola finale alla fine di un array_initializer. Questa sintassi offre flessibilità nell'aggiunta o nell'eliminazione di membri da tale elenco e semplifica la generazione di computer di tali elenchi. nota finale
ECMA C# draft specification