Ufa, já faz 2 semanas que eu falei de C# 4. Estou finalizando uma edição da .Net Magazine e o grupo de arquitetura, o .Net Architects, tem tomado meu tempo (para assuntos bem legais, é verdade, ainda assim…). Assim que o fechamento da .Net Magazine passar tenho um monte de coisas para escrever por aqui. Enfim, vamos lá.

Se você está chegando agora, este é o quarto post sobre o assunto. Se você não viu os posts anteriores:

  1. C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
  2. C# 4.0: Uma linguagem dinâmica – onde apresentei as novidades do C# 4.0 que vão aproximá-lo mais de uma linguagem dinâmicas.
  3. C# 4.0: Teremos covariância e contravariância – onde apresentei a tão esperada variância do C#.

O assunto que vou tratar aqui hoje, argumentos opcionais e nomeados, é mais uma feature para o pessoal reclamar que o C# está ficando parecido com o VB. Se você está reclamando já adianto, C# e VB são linguagens irmãs, são dialetos de uma mesma lingua, e, segundo a Microsoft, vão ficar mais e mais parecido ao longo dos anos. Tudo que tiver em um vai ter no outro (o que for possível, ao menos). Não vou ficar falando que VB é uma linguagem tão profissional quanto C#, e que as duas têm prós e contras, porque já disse isso aqui antes, em um dos posts da série Polêmicas (para a qual já tenho uns 5 ou 6 assuntos guardados). Fato é: as linguagens todas vão ficar mais dinâmicas. É a influência do Ruby (e do LINQ) sobre o .Net. Você não precisa usar, mas vai perder um monte se não usar.

Na linha deste tipo de preocupação, na última reunião do grupo, o André Dias e eu estávamos conversando sobre essa história do C# vir com dinamismo, e que tem gente achando que a palavra-chave dynamic é igual a um “Dim”. Não é. Depois aproveito e posto sobre isso também.

Indo então ao ponto: argumentos opcionais e nomeados. Antes, se você tinha um método, e quisesse que algum parâmetro fosse opcional, você fazia um overload:

    class Boliche
    {
        public void Derrubar(int pinos)
        {
        }
        public void Derrubar()
        {
            this.Derrubar(0);
        }
    }

Assim, chamar Boliche.Derrubar(), sem parâmetros, é o mesmo que chamar Boliche.Derrubar(0), passando zero. Funcionava perfeitamente, só que dava um trabalhão, principalmente se você tivesse um monte de overloads, além de não ser explícito para o consumidor da classe, que tinha que ficar escolhendo qual overload usar e não sabia qual o valor padrão. Afinal, o consumidor não tem acesso ao fonte necessariamente, e onde está escrito que o valor padrão é zero? Pois agora esse problema será resolvido. O C# utilizará uma sintaxe muito parecida com a do VB, onde, após a definição do nome do parâmetro, você coloca um igual e o valor padrão. No VB ainda precisa da palavra-chave “optional”. No C# não precisa. É simples assim, e é uma mão na roda, livrando a gente daquele monte de overloads, para fazer algo tão simples:

    class Boliche
    {
        public void Derrubar(int pinos = 0)
        {
        }
    }

A chamada do cliente ao método fica idêntica.

Parâmetros opcionais não são nada de novo. Existe no .Net (e portanto no CLR e no CLS) desde a versão 1.0, já que eram necessários para o VB funcionar. A IL sempre gerou parâmetros opcionais para o VB. São “novidades” em uma linguagem com release de 8 anos, existente desde o VB com guaraná com rolha… Não entendo porque o C# não tem isso desde o princípio… vai ver o pessoal do Java e do C/C++ que estava migrando ia reclamar que C# era muito parecido com o VB. Vai saber…

Pois bem, isso são parâmetros opcionais. Indo em frente: Argumentos nomeados vêm de mão dadas com parâmetros opcionais. Vejam esse exemplo:

    class Boliche
    {
        public void Derrubar(int pinos = 0, int pinosRestantes = 10)
        {
        }
    }

Posso chamar direto Boliche.Derrubar(), mas isso passaria zero e dez aos parâmetros. E se eu quisesse chamar o método passando somente o segundo argumento, chamado pinosRestantes? Fácil:

        static void Main(string[] args)
        {
            var b = new Boliche();
            b.Derrubar(pinosRestantes: 7);
        }

Neste caso, estou dizendo ao compilador que quero passar somente o segundo parâmetro, e que no primeiro aceito o valor padrão, que é zero. Também muito simples.

Algumas regras:

  1. Parâmetros opcionais são sempre os últimos. Você não pode ter um parâmetro não opcional à direita de um opcional. Faz sentido se você pensar na estruturação da sintaxe.
  2. Somente constantes são aceitas como valores padrão de parâmetros opcionais. Isso pode ser um bom motivo para continuar a usar overloads em alguns casos.
  3. Argumentos nomeados podem ser especificados em qualquer ordem. Você poderia chamar, por exemplo, Boliche.Derrubar(pinosRestantes: 5, pinos: 3), sem problemas. Mas ainda que possam ser especificados em qualquer ordem, eles serão avaliados na ordem escrita no método que está chamando, e não na ordem declarada na função. Isso é importante caso você esteja chamando funções para passar valor aos parâmetros, como Boliche.Derrubar(pinosRestantes: this.ObterPinosRestantes(), pinos: this.ObterPinosDerrubados()). Neste caso, o método ObterPinosRestantes é chamado antes do método ObterPinosDerrubados. Isso pode ser um problema caso você tenha funções com efeitos colaterais, algo que deve ser evitado a todo custo (não crie uma função que retorna um valor, e altera outro no contexto quase de forma escondida, a não ser que queira problemas para depurar depois).
  4. Você pode especificar argumentos não opcionais por nome normalmente. Isso não está restrito a argumentos opcionais.

Essas features são plenamente suportadas no CTP do Visual Studio 2010 que saiu em Outubro, ao contrário das features de variância e dinamismo, que estão pela metade.

No próximo post de C# 4.0 vou falar de interop com COM, e vocês vão ver o quanto isso ajuda neste tipo de caso.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.