C# 4.0: Argumentos Com Nome E Opcionais
Como parte do esforço de co-evolução do C# e do Visual Basic, o C# 4.0 introduz Argumentos com Nome e Opcionais.
Primeiro clarifiquemos o que são argumentos e o que são parâmetros:
De facto, a Especificação da Linguagem C# afirma em §7.5 que:
A lista de argumentos (§7.5.1) da invocação de um membro função providencia os valores que vão ser efectivamente usados como variáveis ou referências a variáveis como parâmetros do membro função.
Dadas as definições acima, podemos afirmar que:
-
Os parâmetros sempre tiveram, e continua a ter, nome.
-
Os parâmetros nunca foram, e continuam a não ser, opcionais.
Argumentos Com Nome
Até agora, a forma como o compilador de C# fazia corresponder os argumentos da declaração de uma chamada a um método era pela sua posição. O primeiro argumento providencia o valor para o primeiro parâmetro o segundo argumento providencia o valor para o segundo parâmetro, e por aí fora, independentemente do número de parâmetros. Se algum parâmetro não tivesse um argumento correspondente para lhe providenciar o valor, isso faria o compilador emitir um erro de compilação.
Para esta chamada:
Greeting("Mr.", "Morgado", 42);
este métdo:
public void Greeting(string title, string name, int age)
receberá como parâmetros:
-
title: “Mr.”
-
name: “Morgado”
-
age: 42
O que esta nova funcionalidade permite é usar os nomes dos parâmteros para identificar os argumentos correspondentes na forma: name:value
Nem todos os argumentos da lista precisam de ser identificados com o nome do correspondente parâmetros. No entanto, todos os argumentos com nome devem ser posicionados no fim da lista de argumentos. A correspondência entre os argumentos (e a avaliação do seu valor) e os parâmetros será feita primeiro pelo nome dos argumentos com nome (apesar destes se encontrarem no fim da lista) e em seguida pela posição, para os parâmteros para os quais ainda não foi atribuído valor.
Isto quer dizer que, para esta definição de método:
public void Method(int first, int second, int third)
esta declaração de chamada:
int i = 0;
Method(i, third: i++, second: ++i);
terá o seguinte código gerado pelo compilador:
int i = 0;
int CS$0$0000 = i++;
int CS$0$0001 = ++i;
Method(i, CS$0$0001, CS$0$0000);
o que dará aos parâmetros do método os seguintes valores:
- first: 2
- second: 2
- third: 0
Note-se que os nomes das variáveis geradas pelo compilador não são identificadores C# válidos. No entanto, são identificadores .NET válidos evitando, assim, colisões de nomes entre o código gerado pelo compilador e o código escrito pelo programador.
Para além de permitir reordenar a lista de argumentos, esta funcionalidade apresenta-se muito útil para documentar código que quando, por exemplo, a lista de argumentos é muito extensa ou é pouco claro o significado dos argumentos na declaração da chamada ao método.
Argumentos Opcionais
Agora os parâmetros pode ter valores por omissão:
public void Method(int first, int second = 2, int third = 3)
Os parâmetros com valores por omissão têm de ser os últimos da lista de parâmetros e esse valor é usado se o argumento correspondente estiver em falta na lista de argumentos da declaração da chamado ao método.
Para esta declaração de chamada:
int i = 0;
Method(i, third: ++i);
terá o seguinte código gerado pelo compilador:
int i = 0;
int CS$0$0000 = ++i;
Method(i, 2, CS$0$0000);
o que dará aos parâmetros do método os seguintes valores:
- first: 1
- second: 2
- third: 1
Porque, quando os parâmetros do método têm valores por omissão, os arumentos podem ser omitidos na declaração da chamada ao método, pode parecer-se com sobreposição de métodos, mas não é.
Embora métodos como este:
public StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = true,
int bufferSize = 1024)
permitam que as sua chamadas sejam escritas assim:
OpenTextFile("foo.txt", Encoding.UTF8);
OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);
OpenTextFile(
bufferSize: 4096,
path: "foo.txt",
detectEncoding: false);
O compilador trata os valores por omissão como trata os campos constantes tomando o seu valor e usando-o em vez de uma referência ao valor. Portanto, tal como com os campos constantes, os métodos com parâmetros com valores por omissão não devem ser expostos publicamente (não esquecer que membros internos podem ser aceiddos publicamente – InternalsVisibleToAttribute). Se tais métodos forem acedidos publicamente por outra assemby, esses valores serão codificados no código chamador e, se a assembly chamada tiver esses valores modificados, essa modificação não se reflectirá no código já compilado.
À primeira vista, pensei que usar os argumentos opcionais para chamar “mau” código era bom, mas a possibilidade de definir esse tipo de métodos era terrível. Mas depois apercebi-me que, uma vez que uso campos constantes, não será mau usar métodos com parâmetros com valores por omissão pra métodos de acesso privado.