Arrays monodimensionais e multidimensionais no C#

Arrays multidimensionais e vetores são as formas mais básicas de guardar vários valores do mesmo tipo em uma única variável no C#. Neste artigo, você vai entender como declarar, inicializar, ler, percorrer e copiar arrays monodimensionais (vetores) e multidimensionais (matrizes), com exemplos práticos e as armadilhas que mais confundem quem está começando.

O que são arrays monodimensionais e multidimensionais

Um array é uma estrutura que representa uma lista de itens no C#. Todos os itens pertencem ao mesmo tipo, e você acessa cada um deles apontando a posição desejada.

Existem dois formatos principais que vamos estudar aqui. O array monodimensional, também chamado de vetor, funciona como uma fila de valores, enquanto os arrays multidimensionais, também chamados de matrizes, funcionam como tabelas com linhas e colunas.

Além disso, o C# oferece um terceiro formato, o array irregular (jagged array), que é um array de arrays. Vamos compará-lo com os arrays multidimensionais mais adiante.

Antes de começar, vale conhecer algumas características que valem para qualquer array no C#:

  • Tipagem forte: depois de definir o tipo do array, ele só aceita valores daquele tipo.
  • Tamanho fixo: você define o tamanho na criação e não consegue alterá-lo depois.
  • Índice começando em zero: um array com n elementos vai da posição 0 até a posição n-1.
  • Tipo de referência: todo array deriva da classe abstrata System.Array e vive na memória gerenciada.

Segundo a documentação oficial da Microsoft sobre arrays, o número de dimensões fica definido na declaração da variável. Por outro lado, o tamanho de cada dimensão só aparece quando você cria a instância.

Se você ainda não tem um projeto para testar os exemplos, o artigo sobre como criar projetos .NET via console mostra como montar um aplicativo de console em poucos comandos.

Array monodimensional (vetor)

O vetor é o formato mais simples de array. Na prática, ele se parece com uma fileira de caixas numeradas, em que cada caixa guarda um único valor.

Representação de um vetor como uma fileira horizontal de quadrados, cada um correspondendo a uma posição do array
Um vetor organiza os valores em uma única linha, acessada por um índice.

Declarando o vetor

Para declarar um vetor, você informa o tipo dos elementos e a quantidade de posições:

// Vetor com 10 posições do tipo int
var singleDimensionalArray = new int[10];

Lembre que os arrays no C# começam na posição zero. Sendo assim, um array de 10 elementos fica com as posições 0, 1, 2 e assim por diante, até 8 e 9.

O C# é uma linguagem fortemente tipada. Isso significa que, uma vez definido o tipo do array, o compilador só aceita valores desse mesmo tipo.

Também vale saber que o C# preenche as posições não inicializadas com o valor padrão do tipo. Em um array de int, por exemplo, todas as posições começam com 0, enquanto em um array de string começam com null.

Inicializando o vetor

Você pode inicializar o array já na declaração, informando os valores entre chaves:

// Declaração e inicialização na mesma linha
var singleDimensionalArray = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Nesse caso, a quantidade de valores precisa bater com o tamanho informado. Caso contrário, o compilador acusa um erro.

Outra opção é atribuir o valor de cada posição depois da criação:

// A primeira posição (índice 0) recebe o valor 61
singleDimensionalArray[0] = 61;

O valor entre colchetes representa a posição do array que vai receber o valor. Ou seja, no exemplo acima a posição zero recebe 61, e ao ler a primeira posição do array teremos 61 como retorno.

Lendo itens do vetor

Ler o valor de um elemento é bem simples. Basta chamar o array e passar entre colchetes a posição desejada:

// Exibe o valor da primeira posição
Console.WriteLine($"{singleDimensionalArray[0]}");

Assim, o console exibe o valor guardado na posição indicada. Se você informar uma posição que não existe, como singleDimensionalArray[10] em um vetor de 10 elementos, o runtime lança uma IndexOutOfRangeException.

Percorrendo o vetor com for e foreach

Você pode usar tanto a instrução for quanto a instrução foreach para percorrer o array. Dessa forma, o código executa uma ação a cada nova posição visitada.

Primeiro, vamos atribuir valores a cada posição do array:

// Cada posição recebe o próprio número do índice
for (var index = 0; index < singleDimensionalArray.Length; index++)
    singleDimensionalArray[index] = index;

No exemplo acima, a propriedade Length devolve o total de elementos. Então, a posição zero recebe o valor 0, a posição um recebe o valor 1, e assim por diante.

Em seguida, percorremos o array e exibimos o valor guardado em cada posição:

// Leitura com for, usando o índice
for (var index = 0; index < singleDimensionalArray.Length; index++)
    Console.WriteLine(singleDimensionalArray[index]);

O foreach simplifica a leitura, porque entrega diretamente o valor de cada elemento:

// Leitura com foreach, usando o valor
foreach (var item in singleDimensionalArray)
    Console.WriteLine(item);

Repare que, dentro do foreach, a variável item já é o valor, e não a posição. Por isso, escrever singleDimensionalArray[item] é um erro comum, que só funciona por coincidência quando os valores são iguais aos índices.

Na dúvida, use for quando precisar da posição (para alterar o valor, por exemplo) e foreach quando só precisar ler os itens.

Métodos úteis da classe Array

Como todo array deriva de System.Array, os vetores ganham métodos estáticos prontos para tarefas comuns. Assim, você não precisa escrever à mão rotinas de ordenação ou de busca.

var scores = new int[] { 42, 7, 19, 88, 3 };

// Ordena os elementos em ordem crescente
Array.Sort(scores);

// Inverte a ordem dos elementos
Array.Reverse(scores);

// Procura a posição de um valor (retorna -1 se não encontrar)
var position = Array.IndexOf(scores, 19);

Console.WriteLine(string.Join(", ", scores)); // 88, 42, 19, 7, 3
Console.WriteLine(position);                  // 2

Atenção, porém, porque Array.Sort, Array.Reverse e Array.IndexOf trabalham apenas com vetores. Se você passar arrays multidimensionais para esses métodos, o runtime lança uma RankException.

Por isso, ao ordenar os dados de uma matriz, normalmente você copia uma linha para um vetor, ordena e devolve os valores. Esse limite ajuda a entender por que vetores e arrays multidimensionais têm usos diferentes no dia a dia.

Copiando um vetor com Clone

Quando precisar copiar um array, prefira o método Clone(). Isso acontece porque o operador = não copia os elementos, ele apenas cria uma nova referência para o mesmo array.

Veja a diferença na prática:

var original = new int[] { 1, 2, 3 };

// Apenas uma nova referência para o mesmo array
var sameArray = original;
sameArray[0] = 99;
Console.WriteLine(original[0]); // 99

// Uma cópia independente dos elementos
var copy = (int[])original.Clone();
copy[1] = 50;
Console.WriteLine(original[1]); // 2

Como Clone() devolve um object, você precisa fazer a conversão para o tipo do array. Além disso, a cópia é rasa: em um array de objetos, os dois arrays continuam apontando para as mesmas instâncias.

Para entender melhor a diferença entre cópia rasa e cópia profunda, vale a leitura do artigo clonando objetos no C#.

Arrays multidimensionais (matriz)

Os arrays multidimensionais organizam os dados como uma tabela. Assim, em vez de um único índice, cada elemento passa a ter uma linha e uma coluna.

Graficamente, podemos representar uma matriz 3x2, com três linhas e duas colunas, desta forma:

Tabela de quadrados com três linhas e duas colunas representando arrays multidimensionais no C#
Uma matriz 3x2 organiza os valores em linhas e colunas.

Declarando arrays multidimensionais

Para declarar a matriz da imagem como um array multidimensional 3x2, informamos a quantidade de linhas e de colunas separadas por vírgula:

var rows = 3;
var columns = 2;

// Matriz com 3 linhas e 2 colunas
var multidimensionalArray = new int[rows, columns];

Também é possível declarar de forma mais direta:

var multidimensionalArray = new int[3, 2];

A vírgula dentro dos colchetes indica a quantidade de dimensões. Portanto, int[,] representa duas dimensões, enquanto int[,,] representa três.

Inicializando a matriz

A inicialização na declaração varia de acordo com a dimensão da matriz. Em uma matriz 3x2, cada par de chaves internas representa uma linha:

// Três linhas, cada uma com duas colunas
var multidimensionalArray = new int[3, 2] { { 16, 52 }, { 91, 43 }, { 77, 28 } };

Outra opção é adicionar o valor de cada posição depois, assim como fizemos no vetor:

multidimensionalArray[0, 0] = 16; // primeira linha, coluna um
multidimensionalArray[0, 1] = 52; // primeira linha, coluna dois
multidimensionalArray[1, 0] = 91; // segunda linha, coluna um
multidimensionalArray[1, 1] = 43; // segunda linha, coluna dois
multidimensionalArray[2, 0] = 77; // terceira linha, coluna um
multidimensionalArray[2, 1] = 28; // terceira linha, coluna dois

Observe que, diferente do vetor, atribuir valor à matriz exige informar a linha e a coluna do item. Em uma matriz 2D, pense no índice da esquerda como a linha e no da direita como a coluna.

Lendo itens da matriz

Ler o valor de um item da matriz é tão simples quanto ler de um vetor. Basta chamar a matriz e passar entre colchetes a linha e a coluna desejadas:

// Exibe o valor da primeira linha e primeira coluna (16)
Console.WriteLine(multidimensionalArray[0, 0]);

Com isso, o console exibe o valor da posição indicada. Da mesma forma que no vetor, uma linha ou coluna inexistente gera uma IndexOutOfRangeException.

Percorrendo arrays multidimensionais

Com a matriz definida, podemos percorrer os elementos usando a instrução foreach para exibir os valores no console:

// Percorre todos os elementos, linha por linha
foreach (var item in multidimensionalArray)
    Console.WriteLine($"{item}");

Nos arrays multidimensionais, o foreach incrementa primeiro o índice da dimensão mais à direita. Em outras palavras, ele lê a primeira linha inteira, depois a segunda, e assim por diante, exibindo 16, 52, 91, 43, 77 e 28.

No entanto, o foreach não informa em qual linha e coluna está cada valor. Quando você precisa dessas posições, use dois laços for aninhados com o método GetLength:

// GetLength(0) devolve o número de linhas e GetLength(1) o de colunas
for (var row = 0; row < multidimensionalArray.GetLength(0); row++)
{
    for (var column = 0; column < multidimensionalArray.GetLength(1); column++)
        Console.Write($"{multidimensionalArray[row, column]} ");

    Console.WriteLine();
}

Nesse caso, a saída mostra a matriz em formato de tabela, com uma linha do console para cada linha da matriz.

Atenção a um detalhe importante: em arrays multidimensionais, a propriedade Length devolve o total de elementos de todas as dimensões. Na matriz 3x2, por exemplo, Length vale 6, e por isso não serve como limite do laço de linhas.

Arrays multidimensionais com três dimensões

Os arrays multidimensionais não param em duas dimensões. Você pode criar estruturas com três ou mais, acrescentando vírgulas na declaração:

// Duas "camadas", cada uma com 2 linhas e 3 colunas
var cube = new int[2, 2, 3]
{
    { { 1, 2, 3 }, { 4, 5, 6 } },
    { { 7, 8, 9 }, { 10, 11, 12 } }
};

Console.WriteLine(cube[1, 0, 1]); // 8
Console.WriteLine(cube.Rank);     // 3
Console.WriteLine(cube.Length);   // 12

A propriedade Rank informa a quantidade de dimensões do array. Já o método GetLength, que vimos antes, devolve o tamanho de uma dimensão específica.

Na prática, matrizes com três dimensões aparecem em cenários como grades de voxels, cubos de dados ou camadas de um mapa. Contudo, acima disso o código fica difícil de ler, e geralmente vale modelar os dados com classes.

Arrays multidimensionais versus arrays irregulares

Além dos arrays multidimensionais, o C# oferece os arrays irregulares, também chamados de jagged arrays. Embora pareçam semelhantes, os dois funcionam de forma bem diferente.

Um array irregular é um array cujos elementos são outros arrays. Por isso, cada linha pode ter um tamanho diferente:

// Array de arrays: cada linha tem um tamanho próprio
var jaggedArray = new int[3][];

jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 };
jaggedArray[1] = new int[] { 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 11, 22 };

// Acesso com dois pares de colchetes
Console.WriteLine(jaggedArray[0][1]); // 3

Repare na sintaxe de acesso. Enquanto a matriz usa [linha, coluna], o array irregular usa [linha][coluna], porque primeiro acessa o array interno e depois o elemento dentro dele.

Outra diferença está na inicialização. Cada linha de um array irregular começa como null, então você precisa criá-la antes de usar, caso contrário recebe uma NullReferenceException.

Para resumir as diferenças:

Característica Array multidimensional Array irregular
Declaração int[,] int[][]
Acesso matrix[1, 0] jagged[1][0]
Tamanho das linhas Todas iguais Cada linha com o seu
Inicialização das linhas Automática Manual, começa com null
Bom para Tabelas e grades regulares Listas de tamanhos variados

Então, se os dados formam uma tabela retangular, os arrays multidimensionais deixam o código mais claro. Por outro lado, quando cada linha tem um tamanho diferente, o array irregular evita desperdício de posições vazias.

Erros comuns ao usar arrays multidimensionais

Alguns erros aparecem com frequência em quem está começando com vetores e arrays multidimensionais. Conhecer cada um deles economiza bastante tempo de depuração.

O primeiro é esquecer que o índice começa em zero. Em um array de 10 posições, a última é a 9, e tentar acessar a posição 10 gera uma IndexOutOfRangeException.

O segundo é usar Length como limite das linhas de uma matriz. Como vimos, em arrays multidimensionais essa propriedade devolve o total de elementos, e o correto é usar GetLength(0) para as linhas e GetLength(1) para as colunas.

O terceiro é tratar o valor do foreach como se fosse um índice. Dentro do laço, a variável já contém o elemento, então basta usá-la diretamente.

O quarto é copiar arrays com o operador =. Nesse caso, as duas variáveis apontam para o mesmo array, e uma alteração em uma aparece na outra.

Por fim, outro deslize comum é tentar aumentar o tamanho de um array depois de criado. Como o tamanho é fixo, você precisa criar um novo array ou usar uma coleção dinâmica, como List<T>.

Agora é com você

Para exercitar o que você viu neste artigo, experimente criar vetores de objetos e matrizes com mais de duas dimensões. Por exemplo, monte um vetor de nomes de produtos e uma matriz com as vendas de cada produto por mês.

Outro exercício interessante é exibir um vetor ou uma matriz de forma gráfica no console, como um tabuleiro. Afinal, isso envolve mais complexidade do que apenas declarar e percorrer arrays multidimensionais em um contexto estático.

Algumas ideias para praticar:

  • Calcular a soma de cada linha e de cada coluna de uma matriz 3x3.
  • Montar um tabuleiro de jogo da velha com um char[3, 3].
  • Transpor uma matriz, trocando linhas por colunas.
  • Comparar o mesmo exercício usando um array irregular.

Conclusão

Vetores e arrays multidimensionais formam a base para guardar coleções de tamanho fixo no C#. Com eles, você organiza valores do mesmo tipo em linhas ou tabelas e acessa cada item de forma direta pelo índice.

Neste artigo, você aprendeu a declarar, inicializar, ler e percorrer vetores e matrizes, além de copiar arrays com Clone(). Também conheceu os arrays irregulares e os erros mais comuns ao trabalhar com arrays multidimensionais.

Portanto, o próximo passo é praticar. Quanto mais exercícios você fizer, mais natural vai ficar escolher entre vetor, matriz, array irregular ou uma coleção dinâmica.

FAQ

Qual a diferença entre vetor e matriz no C#?

O vetor é um array monodimensional, com um único índice. Já a matriz é um array multidimensional, que usa dois ou mais índices, como linha e coluna.

Como descobrir o número de linhas e colunas de uma matriz?

Use o método GetLength(0) para as linhas e GetLength(1) para as colunas. A propriedade Length devolve o total de elementos.

Posso mudar o tamanho de um array depois de criado?

Não, o tamanho de um array é fixo. Para coleções que crescem, use List<T> ou crie um novo array maior.

Qual a diferença entre int[,] e int[][]?

O int[,] é um array multidimensional retangular, com todas as linhas do mesmo tamanho. O int[][] é um array irregular, em que cada linha é um array independente.

O método Clone faz uma cópia profunda?

Não, o Clone() faz uma cópia rasa. Em arrays de objetos, as duas cópias continuam apontando para as mesmas instâncias.