C# - Usando Array de argumentos (params)


Antes de entrar diretamente no assunto dos Array de argumentos vamos recordar o conceito de sobrecarga ou overloading.

A sobrecarga é um termo técnico para declarar dois ou mais métodos com o mesmo nome no mesmo escopo; ela é muito útil em casos onde você quer realizar a mesma ação com argumentos de tipos diferentes. O exemplo clássico de overloading na linguagem C# é o método Console.Writeline; este método possui muitas sobrecargas de maneira que você pode passar qualquer tipo de argumento primitivo. Abaixo vemos algumas sobrecargas do método Console.Writeline:

class Console
{
   public static void WriteLine(Int32 value)
   public static void WriteLine(Double value)
   public static void WriteLine(Decimal value)
   public static void WriteLine(Boolean value)
   public static void WriteLine(String value)
...
}

No entanto a sobrecarga não trata a situação quando o número de parâmetros varia mas o seu tipo não. Suponha que você precise escrever muitos valores usando o método Console.Writeline. Você terá que fornecer várias versões do método que possam usar dois parâmetros usando várias combinações, outras versões podem usar 3 parâmetros e assim por diante. O que seria algo realmente muito enfadonho.

Felizmente, existe uma maneira de escrever um método que usa um número variável de argumentos.

Você pode usar uma matriz de parâmetros (um parâmetro declarado com a palavra-chave params).

Usando um array de argumentos

Suponha que você queira escrever um método para determinar o valor mínimo em um conjunto de valores passados como parâmetros.

Uma maneira é usar um array.

Por exemplo, para encontrar o menor dos vários valores do tipo int, você poderia escrever um método estático chamado Minimo com um único parâmetro que representa um conjunto de valores int:

             public static int Minimo(int[] paramLista)
            {
                // Verifica se o chamaador forneceu ao menos um parâmetro
                // Se não forneceu, lança uma exceção do tipo ArgumentException – 
                // Não é possivel encontrar o menor valor em uma lista vazia
                if (paramLista == null || paramLista.Length == 0)
                {
                    throw new ArgumentException("Util.Minimo: parâmetros insuficientes...");
                }
                // Define o valor do minimo atual na lista de parâmetros para o primeiro item
                int minimoAtual = paramLista[0];
                // Percorre a lista de parâmetros procurando qualquer item menor que o valor minimo atual
                foreach (int i in paramLista)
                {
                    // Se encontrar um item que seja menor que o valor em minimoAtual então atribui esse valor a minimoAtual
                    if (i < minimoAtual)
                    {
                        minimoAtual = i;
                    }
                }
                // Ao final do laço o valor de minimoAtual irá conter o menor valor
                // do item da lista de parâmetros que será retornado
                return minimoAtual;
            }

Para usar o método Minimo para encontrar o valor mínimo de duas variáveis inteiras chamadas, fazemos assim:

 static void Main(string[] args)
 {
      int[] array = new int[2];
      array[0] = 10;
      array[1] = 7;
      int min = Util.Minimo(array);
      Console.WriteLine("O valor minimo é : " + min);
      Console.ReadKey();
 }

Para calcular o valor mínimo para 3 variáveis faríamos assim:

 static void Main(string[] args)
 {
      int[] array = new int[3];
      array[0] = 10;
      array[1] = 7;
      array[2] = 11;
      int min = Util.Minimo(array);
      Console.WriteLine("O valor minimo é : " + min);
      Console.ReadKey();
 }

Embora a nossa solução evite a necessidade de usar um grande número de sobrecargas temos que escrever código adicional para preencher o array com os valores que estamos usando a que cada vez que o número de elementos muda. Poderíamos tornar o código mais limpo usando um array anônimo :

 static void Main(string[] args)
  {
        int min = Util.Minimo(new int[] { 11,3,7,12,10 });
        Console.WriteLine("O valor minimo é : " + min);
        Console.ReadKey();
  }

Melhorou !!! mas ainda temos que criar e popular um array.

Então o que podemos fazer ??

A solução é permitir que o compilador escreva o código para você usando um array de parâmetros como parâmetro para o método Minimo.

Declarando um array de parâmetros (params array)

Um array de parâmetros permite passar um número variável de argumentos para um método. Você indica um array de parâmetros usando a palavra-chave params como um modificador de parâmetro de um array quando você define os parâmetros do método.

class Util
{
     public static int Minimo(params int[] paramLista)
     {
      // codigo usado anteriormente
     }
}

O efeito da palavra-chave params no método Minimo é que ele permite chamar qualquer número de argumentos inteiros sem se preocupar com a criação de uma matriz.

Por exemplo, para encontrar o mínimo de cinco valores inteiros, podemos fazer assim:

int min = Util.Min(12,3,7,10,5);

O compilador irá traduzir esta chamada para o seguinte código:

int[] array = new int[5];
array[0] = 12;
array[1] = 3;
array[2] = 7;
array[3] = 10;
array[4] = 5;
int min = Util.Minimo(array);

O compilador apenas conta o número de argumentos, cria um array de inteiros deste tamanho, preenche o array com os argumentos e então chama o método passando o único array de parâmetros.

Cabe destacar os seguintes aspectos sobre o uso do params array:

1 - Você não pode usar a palavra-chave params com arrays multidimensionais. O código no exemplo a seguir não irá compilar:
// Erro em tempo de compilação
public static int Minimo(params int [,] tabela)
 
2- Você não pode sobrecarregar um método baseado unicamente na palavra-chave params.
A palavra-chave params não faz parte da assinatura de um método:

// Erro em tempo de compilação: declaração duplicada
public static int Minimo(int[] paramLista)

public static int Minimo(params int [] paramLista)

 
3 - Você não tem permissão para especificar o modificador ref ou o modificaro out com params array:
// Erro em tempo de compilação
public static int Minimo(ref params int[] paramLista)
...
public static int Minimo(out params int[] paramLista)
...
 
4 - Um params array deve ser o último parâmetro. Isso significa que você só pode ter um params array por método:
// Erro em tempo de compilação
public static int Minimo(params int[] paramLista, int i)
 
5 - Um método que não usa params array sempre tem prioridade sobre um método params array. Isso significa que se você quiser,
você ainda pode criar uma versão sobrecarregada de um método para os casos comuns.
Por exemplo:
public static int Minimo(int valor1,int valor2)
...
public static int Minimo(params int[] paramLista)
 

Usando params object[]

Um array de parâmetros do tipo int é muito útil, pois permite que você passe qualquer número de argumentos int em uma chamada de método.

Mas e se além do número de argumentos variar o tipo dos argumentos também variar ?

Neste caso usamos o conceito que se baseia no fato de que object é a raiz de todas as classes e que o compilador pode gerar o código que converte tipos de valor (coisas que não são classes) para objetos usando o boxing.

Obs: Para mais detalhes de boxing e unboxing veja o meu artigo: Box e Unboxing - Macoratti.net

Podemos usar um array de parâmetros do tipo object para declarar um método que aceita qualquer número de argumentos do tipo object permitindo que os argumentos passados sejam de qualquer tipo.

No exemplo abaixo temos a classe Util com dois métodos:

  1. O método listaObjetos - que define o argumento lista como sendo do tipo params object[] - temos um array de parâmetros do tipo object
  2. O método listaInteiros - que define o argumento lista como sendo do tipo params int[] - temos um array de parâmetros do tipo int
 public class Util
 {
            public static void listaObjetos(params object[] lista)
            {
                for (int i = 0; i < lista.Length; i++)
                {
                    Console.Write(lista[i] + " ");
                }
                Console.WriteLine();
            }
            
            public static void listaInteiros(params int[] lista)
            {
                for (int i = 0; i < lista.Length; i++)
                {
                    Console.Write(lista[i] + " ");
                }
                Console.WriteLine();
            }
  }

Veja abaixo como podemos usar estes métodos:

static void Main(string[] args)
 {
            // você pode enviar uma lista de argumentos separados por vírgula 
            // de um tipo especificado
            Util.listaObjetos(1, 2, 3, 4);
            Util.listaObjetos(1, 'a', "teste");

            // Um parâmetro params aceita zero ou mais argumentos
            // A seguinte instrução exibe somente uma linha em branco
            Util.listaObjetos();

            // Um array de argumentos pode ser passado como parãmetro desde que 
            // o tipo coincida com o tipo do parãmetro do método sendo chamado
            int[] arrayInteiros = { 5, 6, 7, 8, 9 };
            Util.listaInteiros(arrayInteiros);

            object[] arrayObjetos = { 2, 'b', "teste", "macoratti" };
            Util.listaObjetos(arrayObjetos);

            // A seguinda chamada causa um erro de compilador porque o array de objetos 
            // não pode ser convertido para um array de inteiros
            //listaInteiros(arrayObjetos); 

            //  A seguinte chamada não causa um erro mas o array inteiro torna-se o primeiro
            // elemento de um array params
            Util.listaObjetos(arrayInteiros);
}

Pegue o projeto completo aqui: ArrayParametros.zip

João 6:44 Ninguém pode vir a mim, se o Pai que me enviou não o trouxer; e eu o ressuscitarei no último dia.

João 6:45 Está escrito nos profetas: E serão todos ensinados por Deus. Portanto todo aquele que do Pai ouviu e aprendeu vem a mim.

Referências:


José Carlos Macoratti


Comentários... powered by macoratti.net