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:
- C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
- 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.
- 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:
- 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.
- 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.
- 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).
- 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.
aa16ac3c-1ee5-4e2e-99bf-d2adb447ebb1|0|.0