Partilhar via


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ção R e um tipo Tde elemento não-matriz.
  • Um tipo de matriz do formulário T[R][R₁]...[Rₓ] é uma matriz com classificação R e um tipo T[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 de int. 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 de object[] para IList<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 que oa1 faz referência a um object[] e não a um string[]. No entanto, o elenco (IList<string>)oa2 não fará com que uma exceção seja lançada, uma vez que oa2 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, longulong, 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).BA 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 que value é uma null referência ou uma referência a um objeto de um tipo que é compatível com o tipo de elemento real de arrayFill . Em Main, as duas primeiras invocações de Fill sucesso, mas a terceira invocação faz com que um System.ArrayTypeMismatchException seja lançado ao executar a primeira atribuição para array[i]. A exceção ocorre porque uma caixa int não pode ser armazenada em uma string 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 para z 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