17 Matrizes
17.1 Generalidades
Uma matriz é uma estrutura de dados que contém várias variáveis que são acessadas por meio de índices computados. As variáveis contidas em uma matriz, também chamadas de elementos da matriz, são todas do mesmo tipo, e esse tipo é chamado de tipo de elemento da matriz.
Uma matriz tem uma classificação que determina o número de índices associados a cada elemento da matriz. A classificação de uma matriz também é referida como as dimensões da matriz. Uma matriz com uma classificação de um é chamada de matriz unidimensional. Uma matriz com uma classificação maior que uma é chamada de matriz multidimensional. Matrizes multidimensionais de tamanho específico são frequentemente referidas como matrizes bidimensionais, matrizes tridimensionais e assim por diante. Cada dimensão de uma matriz tem um comprimento associado que é um número integral maior ou igual a zero. Os comprimentos de dimensão não fazem parte do tipo da matriz, mas são estabelecidos quando uma instância do tipo de matriz é criada em tempo de execução. O comprimento de uma dimensão determina o intervalo válido de índices para essa dimensão: Para uma dimensão de comprimento N
, os índices podem variar de 0
a N – 1
inclusivo. O número total de elementos em uma matriz é o produto dos comprimentos de cada dimensão na matriz. Se uma ou mais das dimensões de uma matriz tiverem um comprimento de zero, diz-se que a matriz está vazia.
O tipo de elemento de uma matriz pode ser um tipo de matriz (§17.2.1). Tais matrizes de matrizes são distintas de matrizes multidimensionais e podem ser usadas para representar "matrizes irregulares".
Exemplo:
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
Exemplo final
Cada tipo de matriz é um tipo de referência (§8.2). O tipo de elemento de uma matriz pode ser qualquer tipo, incluindo tipos de valor e tipos de matriz.
17.2 Tipos de matriz
17.2.1 Generalidades
As produções gramaticais para tipos de matrizes são fornecidas no §8.2.1.
Um tipo de matriz é escrito como um non_array_type seguido por um ou mais rank_specifiers.
Um non_array_type é qualquer tipo que não seja em si um array_type.
A classificação de um tipo de matriz é dada pela rank_specifier mais à esquerda na array_type: Uma rank_specifier indica que a matriz é uma matriz com uma classificação de um mais o número de tokens ",
" na rank_specifier.
O tipo de elemento de um tipo de matriz é o tipo que resulta da exclusão do rank_specifier mais à esquerda:
- Um tipo de matriz do formulário
T[R]
é uma matriz com classificaçãoR
e um tipoT
de elemento não-matriz. - Um tipo de matriz do formulário
T[R][R₁]...[Rₓ]
é uma matriz com classificaçãoR
e um tipoT[R₁]...[Rₓ]
de elemento .
Com efeito, os rank_specifiers são lidos da esquerda para a direita antes do tipo final de elemento não-array.
Exemplo: O tipo in
T[][,,][,]
é uma matriz unidimensional de matrizes tridimensionais de matrizes bidimensionais deint
. Exemplo final
Em tempo de execução, um valor de um tipo de matriz pode ser null
ou uma referência a uma instância desse tipo de matriz.
Nota: Seguindo as regras do §17.6, o valor também pode ser uma referência a um tipo de matriz covariante. Nota final
17.2.2 O tipo System.Array
O tipo System.Array
é o tipo base abstrato de todos os tipos de matriz. Existe uma conversão de referência implícita (§10.2.8) de qualquer tipo de array para System.Array
e para qualquer tipo de interface implementado pela System.Array
. Existe uma conversão de referência explícita (§10.3.5) de e qualquer tipo de interface implementado por System.Array
para qualquer tipo de System.Array
matriz. System.Array
não é, em si mesmo, um array_type. Pelo contrário, é um class_type do qual derivam todos os array_types.
Em tempo de execução, um valor de tipo System.Array
pode ser null
ou uma referência a uma instância de qualquer tipo de matriz.
17.2.3 Matrizes e interfaces de coleção genéricas
Uma matriz T[]
unidimensional implementa a interface System.Collections.Generic.IList<T>
(IList<T>
para abreviar) e suas interfaces base. Assim, há uma conversão implícita de T[]
para IList<T>
e suas interfaces de base. Além disso, se houver uma conversão de referência implícita de S
para então S[]
T
implementa IList<T>
e há uma conversão de referência implícita de para IList<T>
e suas interfaces de S[]
base (§10.2.8). Se houver uma conversão de referência explícita de S
para, então há uma conversão de referência explícita de para IList<T>
e suas interfaces de S[]
base (§10.3.5T
).
Da mesma forma, uma matriz T[]
unidimensional também implementa a interface System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
para abreviar) e suas interfaces base. Assim, há uma conversão implícita de T[]
para IReadOnlyList<T>
e suas interfaces de base. Além disso, se houver uma conversão de referência implícita de S
para então S[]
T
implementa IReadOnlyList<T>
e há uma conversão de referência implícita de para IReadOnlyList<T>
e suas interfaces de S[]
base (§10.2.8). Se houver uma conversão de referência explícita de S
para, então há uma conversão de referência explícita de para IReadOnlyList<T>
e suas interfaces de S[]
base (§10.3.5T
).
Exemplo: Por exemplo:
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 } }
A atribuição
lst2 = oa1
gera um erro em tempo de compilação, uma vez que a conversão deobject[]
paraIList<string>
é uma conversão explícita, não implícita. O elenco(IList<string>)oa1
fará com que uma exceção seja lançada em tempo de execução, uma vez queoa1
faz referência a umobject[]
e não a umstring[]
. No entanto, o elenco (IList<string>)oa2
não fará com que uma exceção seja lançada, uma vez queoa2
as referências a .string[]
Exemplo final
Sempre que há uma conversão de referência implícita ou explícita de S[]
para IList<T>
, há também uma conversão de referência explícita de e suas interfaces de IList<T>
base para S[]
(§10.3.5).
Quando um tipo S[]
de matriz implementa IList<T>
, alguns dos membros da interface implementada podem lançar exceções. O comportamento preciso da implementação da interface está além do escopo desta especificação.
17.3 Criação de matrizes
As instâncias de matriz são criadas por array_creation_expression s (§12.8.16.5) ou por declarações de variáveis locais ou de campo que incluem um array_initializer (§17.7). As instâncias de matriz também podem ser criadas implicitamente como parte da avaliação de uma lista de argumentos envolvendo uma matriz de parâmetros (§15.6.2.4).
Quando uma instância de matriz é criada, a classificação e o comprimento de cada dimensão são estabelecidos e, em seguida, permanecem constantes durante todo o tempo de vida da instância. Em outras palavras, não é possível alterar a classificação de uma instância de matriz existente, nem é possível redimensionar suas dimensões.
Uma instância de matriz é sempre de um tipo de matriz. O System.Array
tipo é um tipo abstrato que não pode ser instanciado.
Os elementos de matrizes criadas por array_creation_expressions são sempre inicializados com seu valor padrão (§9.3).
17.4 Acesso a elementos de matriz
Os elementos da matriz são acessados usando expressões element_access (§12.8.11.2) do formulário A[I₁, I₂, ..., Iₓ]
, onde A
é uma expressão de um tipo de matriz e cada Iₑ
uma é uma expressão do tipo int
, , uint
, long
ulong
, ou pode ser implicitamente convertida em um ou mais desses tipos. O resultado de um acesso a um elemento de matriz é uma variável, ou seja, o elemento de matriz selecionado pelos índices.
Os elementos de uma matriz podem ser enumerados usando uma foreach
instrução (§13.9.5).
17.5 Membros da matriz
Cada tipo de matriz herda os membros declarados System.Array
pelo tipo.
17.6 Covariância de matriz
Para quaisquer dois reference_type s e , se existir uma conversão de referência implícita (§10.2.8) ou uma conversão de referência explícita (§10.3.5) de para B
, então a mesma conversão de referência também existe do tipo A[R]
de matriz para o tipo B[R]
de matriz , onde R
é qualquer rank_specifier dado (mas o mesmo para ambos os tipos de A
matriz).B
A
Essa relação é conhecida como covariância de matriz. Covariância de matriz, em particular, significa que um valor de um tipo A[R]
de matriz pode realmente ser uma referência a uma instância de um tipo B[R]
de matriz, desde que exista uma conversão de referência implícita de B
para A
.
Devido à covariância de matrizes, as atribuições a elementos de matrizes de tipo de referência incluem uma verificação em tempo de execução que garante que o valor que está sendo atribuído ao elemento de matriz seja realmente de um tipo permitido (§12.21.2).
Exemplo:
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); } }
A atribuição a
array[i]
no método inclui implicitamente uma verificação em tempo de execução, que garante quevalue
é umanull
referência ou uma referência a um objeto de um tipo que é compatível com o tipo de elemento real dearray
Fill
. EmMain
, as duas primeiras invocações deFill
sucesso, mas a terceira invocação faz com que umSystem.ArrayTypeMismatchException
seja lançado ao executar a primeira atribuição paraarray[i]
. A exceção ocorre porque uma caixaint
não pode ser armazenada em umastring
matriz.Exemplo final
A covariância de matrizes especificamente não se estende a matrizes de value_types. Por exemplo, não existe nenhuma conversão que permita que um seja int[]
tratado como um object[]
.
17.7 Inicializadores de matriz
Os inicializadores de matriz podem ser especificados em declarações de campo (§15.5), declarações de variáveis locais (§13.6.2) e expressões de criação de matriz (§12.8.16.5):
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
Um inicializador de matriz consiste em uma sequência de inicializadores variáveis, delimitada por tokens "{
" e "}
" e separada por tokens ",
". Cada inicializador de variável é uma expressão ou, no caso de uma matriz multidimensional, um inicializador de matriz aninhada.
O contexto no qual um inicializador de matriz é usado determina o tipo de matriz que está sendo inicializada. Em uma expressão de criação de matriz, o tipo de matriz precede imediatamente o inicializador ou é inferido a partir das expressões no inicializador de matriz. Em uma declaração de campo ou variável, o tipo de matriz é o tipo do campo ou variável que está sendo declarado. Quando um inicializador de matriz é usado em uma declaração de campo ou variável,
int[] a = {0, 2, 4, 6, 8};
é simplesmente uma abreviação para uma expressão de criação de matriz equivalente:
int[] a = new int[] {0, 2, 4, 6, 8};
Para uma matriz unidimensional, o inicializador da matriz deve consistir em uma sequência de expressões, cada uma com uma conversão implícita para o tipo de elemento da matriz (§10.2). As expressões inicializam elementos de matriz em ordem crescente, começando com o elemento no índice zero. O número de expressões no inicializador de matriz determina o comprimento da instância de matriz que está sendo criada.
Exemplo: O inicializador de matriz acima cria uma
int[]
instância de comprimento 5 e, em seguida, inicializa a instância com os seguintes valores:a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
Exemplo final
Para uma matriz multidimensional, o inicializador da matriz deve ter tantos níveis de aninhamento quantos as dimensões na matriz. O nível de aninhamento mais externo corresponde à dimensão mais à esquerda e o nível de aninhamento mais interno corresponde à dimensão mais à direita. O comprimento de cada dimensão da matriz é determinado pelo número de elementos no nível de aninhamento correspondente no inicializador da matriz. Para cada inicializador de matriz aninhada, o número de elementos deve ser o mesmo que os outros inicializadores de matriz no mesmo nível.
Exemplo: O exemplo:
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
Cria uma matriz bidimensional com um comprimento de cinco para a dimensão mais à esquerda e um comprimento de dois para a dimensão mais à direita:
int[,] b = new int[5, 2];
e, em seguida, inicializa a instância da matriz com os seguintes valores:
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;
Exemplo final
Se uma dimensão diferente da mais à direita é dada com comprimento zero, as dimensões subsequentes são assumidas como tendo também comprimento zero.
Exemplo:
int[,] c = {};
Cria uma matriz bidimensional com um comprimento de zero para a dimensão mais à esquerda e a dimensão mais à direita:
int[,] c = new int[0, 0];
Exemplo final
Quando uma expressão de criação de matriz inclui comprimentos de dimensão explícitos e um inicializador de matriz, os comprimentos devem ser expressões constantes e o número de elementos em cada nível de aninhamento deve corresponder ao comprimento de dimensão correspondente.
Exemplo: Eis alguns exemplos:
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
Aqui, o inicializador para
y
resulta em um erro em tempo de compilação porque a expressão de comprimento de dimensão não é uma constante e o inicializador paraz
resultados em um erro de tempo de compilação porque o comprimento e o número de elementos no inicializador não concordam.Exemplo final
Nota: C# permite uma vírgula à direita no final de um array_initializer. Essa sintaxe fornece flexibilidade para adicionar ou excluir membros de tal lista e simplifica a geração de máquinas dessas listas. Nota final
ECMA C# draft specification