Novos releases do .NET Framework costumam disponibilizar vários recursos novos, buscando com isto facilitar a vida dos programadores na condução de suas atividades rotineiras. Com a versão 4.0 da plataforma .NET também não foi diferente: a introdução de argumentos (parâmetros) nomeados e opcionais para a implementação de métodos escritos em C# representa um bom exemplo disto.

Desenvolvedores habituados às linguagens Visual Basic e Delphi já empregaram/codificaram, em algum momento, métodos que contam com parâmetros opcionais. Basicamente, este tipo de estrutura permite que alguns dos parâmetros que fazem parte da assinatura de uma operação sejam omitidos. Em tais casos, valores default são assumidos para os parâmetros que não foram indicados de forma explícita em instruções que invocam tais métodos. Até então o C# não dispunha deste tipo de comportamento, fato este que obrigava ao uso da técnica conhecida como sobrecarga.

A sobrecarga é um conceito de Orientação a Objetos que possibilita a existência de vários métodos com um mesmo nome, mas que diferem no entanto quanto ao número de parâmetros, aos tipos destes e/ou àquilo que estas operações retornam.

Já o uso de argumentos nomeados permite que se altere a ordem de passagem dos parâmetros ao se acionar uma operação em C#, sem que o método em questão inverta os valores correspondentes a cada argumento e gere, assim, um erro lógico ou de compilação.

A fim de demonstrar o uso de argumentos opcionais em C#, será considerado inicialmente um exemplo que envolve a sobrecarga de métodos nesta linguagem: o cálculo de ICMS sobre produtos.

O ICMS (Imposto sobre Circulação de Mercadorias e Serviços) é um dos tributos mais comuns, com uma série de regras que variam de um estado a outro. De maneira geral, a obtenção do valor deste imposto parte de uma base de cálculo, a qual pode ou não estar sujeita à aplicação de um desconto (percentual de redução); sobre tal valor é então aplicada a alíquota de ICMS, chegando-se finalmente ao valor que deverá ser recolhido junto aos órgãos competentes.

A aplicação da redução da base de cálculo é opcional, dependendo de critérios como o tipo de produto que está comercializando ou, mesmo, de incentivos fiscais lançados p9r um governo. A própria alíquota de ICMS pode não ser empregada em determinadas situações, pelos mesmos motivos (alguma legislação estadual que resulta na isenção deste tributo).

Na Listagem 1 é apresentado um exemplo de sobrecarga de métodos em C#. Ambas as operações se chamam CalcularICMS, com uma versão mais completa contendo três parâmetros (valor da base para cálculo, valor da alíquota de ICMS em percentual, índice percentual de redução da base), ao passo que a versão mais resumida possui apenas dois argumentos (valor da base e alíquota de ICMS). Uma prática normalmente adotada nestes casos consiste, basicamente, em que o método com menor número de parâmetros invoque a operação mais completa, evitando com isto a duplicação de código a partir do fornecimento de um valor default (zero para o parâmetro vlPercentualReducao, na versão com dois parâmetros de CalcularICMS).

Listagem 1: Exemplo de sobrecarga de métodos


public double CalcularICMS(
    double vlBase, double vlAliquota)
{
    return CalcularICMS(vlBase, vlAliquota, 0);
}

public decimal CalcularICMS(
    double vlBase, double vlAliquota, double vlPercentualReducao)
{
    vlBase = vlBase * (100 - vlPercentualReducao) / 100;
    return (vlBase * vlAliquota / 100);
}

Já na Listagem 2 constam exemplos de utilização de cada uma das implementações do método CalcularICMS. No primeiro caso, é acionada a versão sem redução de base de cálculo (o método correspondente assume na verdade o valor zero para este valor), com o segundo exemplo usando o método em que foram definidos todos os parâmetros possíveis.

Listagem 2: Exemplo de sobrecarga de métodos


// Invocando a versão do método que nao possui redução
// de base de cálculo
double icmsProduto1 = CalcularICMS(100.00, 12.00);
            
// Acionado a operação que possibilita a aplicação de
// um percentual de redução na base de cálculo
double icmsProduto2 = CalcularICMS(300.00, 10.00, 40.00);

Empregar o mecanismo para manipulação de parâmetros opcionais em C# dispensa, assim, a necessidade de existência destes dois métodos sobrecarregados. A Listagem 3 ilustra como ficaria o exemplo da operação que calcula o ICMS já modificada, com a passagem de um valor ao parâmetro vlPercentual sendo opcional: o operador “=” seguido de um valor default (zero neste caso) determina, desse modo, que o argumento considerado não precisa ser obrigatoriamente informado.

Esta modificação permite que ambas as instruções da Listagem 2 continuem a funcionar sem maiores problemas, além de se evitar com isso a necessidade de implementação de dois métodos com diferentes números de parâmetros (algo até então obrigatório em versões anteriores ao .NET Framework 4.0).

Listagem 3: Implementação do método CalcularICMS contando com um parâmetro opcional


public double CalcularICMS(
    double vlBase, double vlAliquota, double vlPercentualReducao = 0)
{
    vlBase = vlBase * (100 - vlPercentualReducao) / 100;
    return (vlBase * vlAliquota / 100);
}

É necessário se atentar, entretanto, para a possibilidade de declaração incorreta de métodos contendo parâmetros opcionais. Na Listagem 4 é demonstrado um exemplo disto. Por mais que no primeiro método haja apenas um parâmetro opcional, ao passo que na segunda versão dois dos argumentos também o são, o número de parâmetros coincide em ambas as situações, bem como o tipo de cada um destes (além do que em termos práticos isto não teria grandes efeitos, com o segundo método sendo mais do que suficiente para atender a qualquer demanda). Isto resulta num erro de compilação, como pode ser observado na Figura 1.

Listagem 4: Utilização inválida de parâmetros opcionais


public double CalcularICMS(
    double vlBase, double vlAliquota, double vlPercentualReducao = 0)
{
    vlBase = vlBase * (100 - vlPercentualReducao) / 100;
    return (vlBase * vlAliquota / 100);
}

public double CalcularICMS(
    double vlBase, double vlAliq = 0, double vlPercRed = 0)
{
    vlBase = vlBase * (100 - vlPercRed) / 100;
    return (vlBase * vlAliq / 100);
}
Erro de compilação ao se declararem métodos com parâmetros opcionais

Figura 1: Erro de compilação ao se declararem métodos com parâmetros opcionais

Outro uso inválido de um parâmetro opcional é detalhado na Listagem 5. Argumentos obrigatórios não podem estar declarados depois de um parâmetro opcional; caso isto aconteça, um erro de compilação será gerado.

Listagem 5: Utilização inválida de parâmetros opcionais


// Exemplo de método em que ocorre a declaração incorreta
// de um parâmetro opcional
public void MetodoInvalido(
    string parametro1, int parametro2 = 0, decimal parametro3)
{
    //...
}

Considerações também devem ser feitas a respeito da implementação de interfaces e da herança de classes abstratas. Se um parâmetro não constar na declaração de um método em uma interface, mas mesmo assim tenha sido definido como opcional na classe concreta que implementa essa estrutura, ocorrerá um erro de compilação. O código apresentado na Listagem 6 ilustra bem essa situação.

Listagem 6: Utilização inválida de parâmetros opcionais na implementação de uma interface


public interface InterfaceExemplo
{
    void MetodoExemplo();
}

// Erro de compilação 
public class ClasseConcretaExemplo : InterfaceExemplo
{
    public void MetodoExemplo(int argumento = 0)
    {
        //...
    }
}

Já o exemplo da Listagem 7 demonstra uma forma válida de se implementar uma interface, corrigindo neste caso o erro que existia no trecho de código anterior. O parâmetro de nome “argumento” existe nas duas estruturas aqui relacionadas, assumindo ainda um valor default na classe concreta resultante.

Listagem 7: Implementando uma interface em conjunto com a definição de um parâmetro opcional


public interface InterfaceExemplo
{
    void MetodoExemplo(int argumento);
}

public class ClasseConcretaExemplo : InterfaceExemplo
{
    public void MetodoExemplo(int argumento = 0)
    {
        //...
    }
}

Dentro desse contexto envolvendo interfaces, outras combinações possíveis para a utilização de parâmetros opcionais são:

  • A definição de um parâmetro como opcional na interface, mas a implementação do mesmo como obrigatório na classe concreta;
  • A atribuição de um valor default na interface, seguida por outro valor diferente na classe concreta que implementa a estrutura inicial.

Classes que herdam de tipos básicos (abstratos ou não) seguem as mesmas diretrizes já discutidas para construções que derivam de interfaces. Na Listagem 8 encontra-se um exemplo de declaração de um parâmetro opcional, com isto acontecendo ao se redefinir uma operação que foi herdada de uma superclasse.

Listagem 8: Exemplo de herança de classe com a especificação de um parâmetro opcional


public class ClasseBaseExemplo
{
    public virtual void MetodoExemplo(int argumento)
    {
        //... 
    }
}

public class ClasseConcretaExemplo : ClasseBaseExemplo
{
    public override void MetodoExemplo(int argumento = 0)
    {
        ...
    }
}

Por fim, resta abordar o uso de parâmetros nomeados. Em versões anteriores, acionar métodos escritos em C# implicava que o valor a ser informado para cada argumento seguisse, obrigatoriamente, a ordem na qual os mesmos foram declarados na assinatura de tais construções.

A possibilidade de se definirem parâmetros como opcionais foi, de certa maneira, um dos fatores que motivou a disponibilização deste recurso. Isto não significa que o uso desta técnica está restrito tão somente a operações em que existam argumentos opcionais: métodos convencionais também podem ser invocados a partir da utilização deste mecanismo. A Listagem 9 apresenta dois métodos, com o primeiro destes valendo-se de parâmetros opcionais em sua definição.

Listagem 9: Exemplo de implementação de um método normal e de outro com parâmetros opcionais


public void MetodoComParametrosOpcionais(
    int parametro1 = 0,
    int parametro2 = 0,
    int parametro3 = 0,
    int parametro4 = 0)

{
    //...
}

public void MetodoConvencional(
    string parametro1,
    string parametro2,
    string parametro3,
    string parametro4)
{
    //...
}

Já na Listagem 10 estão dois exemplos de utilização de parâmetros nomeados. No primeiro caso, dois dos parâmetros são omitidos (“parametro1” e “parametro3”), com a especificação de valores para os outros argumentos (“parametro2” e “parametro4”). A segunda instrução demonstra o uso deste tipo de construção com um método normal, ou seja, que não dispõe de parâmetros que não sejam obrigatórios (a ordem de todos os parâmetros foi invertida, do último para o primeiro).

Conforme demonstrado, a utilização de um argumento nomeado requer que se indique o nome deste elemento, seguindo-se a isso o sinal “:” (“dois pontos) e, por fim, o valor que se estará associando a tal parâmetro. Graças a este novo recurso, parâmetros opcionais podem ser omitidos sem que o runtime do .NET Framework confunda a ordem com a qual valores são atribuídos aos mesmos.

Listagem 10: Exemplo de utilização de parâmetros nomeados


// Invocando método com parâmetros opcionais
MetodoComParametrosOpcionais(
    parametro4: 40,
    parametro2: 20);

// Acionando um método convencional
MetodoConvencional(
    parametro4: "4",
    parametro3: "3",
    parametro2: "2",
    parametro1: "1");

Com isso chegamos ao fim de mais um artigo. Parâmetros opcionais e nomeados são recursos bastante flexíveis e que podem simplificar substancialmente a codificação em C#, sobretudo quando se considera a possibilidade de invocação de um método informando um número variável de parâmetros (e partindo-se da premissa de que aquilo que foi omitido assumirá um valor default). Espero que o que expus neste texto possa auxiliá-lo em suas atividades cotidianas. Até uma próxima oportunidade!