C# Parâmetros Opcionais e Nomeados
Até agora, toda chamada precisava passar um argumento para cada parâmetro, na ordem certa. Duas ferramentas deixam isso mais flexível. Um parâmetro opcional tem um valor padrão, e a chamada pode deixá-lo de fora. Um argumento nomeado diz, na chamada, para qual parâmetro vai cada valor: FazerPedido("Caderno", embrulhar: true). Juntas, elas evitam chamadas compridas e deixam claro o que cada valor quer dizer.
- Como dar um valor padrão a um parâmetro (
int quantidade = 1) e chamar o método com ou sem ele. - As duas regras dos opcionais: vêm depois dos obrigatórios e o padrão tem de ser uma constante.
- Como passar argumentos pelo nome, em qualquer ordem.
- Como pular um opcional do meio e manter os outros.
- Por que
true, falsenuma chamada pedem um nome. - Os erros
CS1737,CS1736,CS1739,CS8323,CS1744,CS1740eCS0839.
Todos os programas desta aula rodaram na máquina fictícia da Ana, na pasta ~/cs, com o .NET 10 e o comando dotnet run arquivo.cs.
A ideia: o formulário já preenchido
O formulário de pedido da lojinha vem com alguns campos já preenchidos a lápis: Quantidade: 1, Embrulhar: não. O cliente só escreve o que quer mudar. E, quando escreve, diz qual campo está preenchendo: "embrulhar: sim". Ninguém precisa preencher os campos na ordem do papel.
| No formulário | Em C# | Nome técnico |
|---|---|---|
| Campo que já vem preenchido a lápis | int quantidade = 1 na declaração | parâmetro opcional, com valor padrão |
| Escrever "Embrulhar: sim", dizendo o campo | embrulhar: true na chamada | argumento nomeado |
1. Parâmetros opcionais
Saudar("Ana");
Saudar("Bruno", "Bom dia");
void Saudar(string nome, string saudacao = "Olá")
{
Console.WriteLine($"{saudacao}, {nome}!");
}
$ dotnet run opc1.cs Olá, Ana! Bom dia, Bruno!
- string saudacao = "Olá"
- Um parâmetro com valor padrão. O
= "Olá"quer dizer: "se a chamada não mandar nada parasaudacao, use"Olá"". Por isso ele é opcional. - string nome
- Um parâmetro comum, sem padrão: é obrigatório. Toda chamada tem de passar um nome.
- Saudar("Ana");
- Só o obrigatório.
saudacaorecebe o padrão,"Olá". - Saudar("Bruno", "Bom dia");
- Os dois. O valor passado vence o padrão.
Um método pode ter vários opcionais. Aqui, a quantidade e o desconto têm padrão:
MostrarItem("Caderno", 12.50m);
MostrarItem("Caneta", 3m, 4);
MostrarItem("Borracha", 1.75m, 2, 10);
void MostrarItem(string produto, decimal preco, int quantidade = 1, int descontoPorcento = 0)
{
decimal total = preco * quantidade * (100 - descontoPorcento) / 100;
Console.WriteLine($"{quantidade} x {produto} (-{descontoPorcento}%) = {total:C}");
}
$ dotnet run opc2.cs 1 x Caderno (-0%) = R$ 12,50 4 x Caneta (-0%) = R$ 12,00 2 x Borracha (-10%) = R$ 3,15
| Chamada | quantidade | descontoPorcento |
|---|---|---|
MostrarItem("Caderno", 12.50m) | 1 (padrão) | 0 (padrão) |
MostrarItem("Caneta", 3m, 4) | 4 | 0 (padrão) |
MostrarItem("Borracha", 1.75m, 2, 10) | 2 | 10 |
Os argumentos continuam indo por posição: o terceiro valor vai para quantidade, o quarto para descontoPorcento. Por isso, só com posição, não dá para mandar o desconto sem mandar também a quantidade. A seção 3 resolve isso.
Missão: a última chamada não passa a quantidade, e o programa não compila (CS7036). Sem mudar as chamadas, dê ao parâmetro quantidade o valor padrão 1.
3 x Caderno = R$ 37,50 1 x Lápis = R$ 0,90
MostrarTotal("Caderno", 12.50m, 3);
MostrarTotal("Lápis", 0.90m);
void MostrarTotal(string produto, decimal preco, int quantidade)
{
Console.WriteLine($"{quantidade} x {produto} = {preco * quantidade:C}");
}
MostrarTotal("Caderno", 12.50m, 3);
MostrarTotal("Lápis", 0.90m);
void MostrarTotal(string produto, decimal preco, int quantidade = 1)
{
Console.WriteLine($"{quantidade} x {produto} = {preco * quantidade:C}");
}
2. As regras dos opcionais
Regra 1: opcionais vêm depois dos obrigatórios
Como os argumentos vão por posição, um opcional no começo não faria sentido: em Saudar("Ana"), o "Ana" iria para o primeiro parâmetro, e o obrigatório ficaria sem nada.
Saudar("Ana");
void Saudar(string saudacao = "Olá", string nome)
{
Console.WriteLine($"{saudacao}, {nome}!");
}
$ dotnet run opcordem.cs /home/ana/cs/opcordem.cs(1,1): error CS7036: Não há nenhum argumento fornecido que corresponda ao parâmetro necessário 'nome' de 'Saudar(string, string)' /home/ana/cs/opcordem.cs(3,49): error CS1737: Os parâmetros opcionais devem aparecer após todos os parâmetros necessários Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O erro que importa é o segundo, CS1737 (Optional parameters must appear after all required parameters), na linha 3: na declaração. O CS7036 da linha 1 é consequência, porque "Ana" foi parar em saudacao. Conserto: void Saudar(string nome, string saudacao = "Olá").
Regra 2: o padrão tem de ser uma constante
O valor padrão é decidido na hora de compilar, e não na hora de rodar. Por isso ele tem de ser um valor fixo: um número, um texto entre aspas, true/false, null ou uma const. Algo que só se sabe com o programa rodando, como a data e a hora de agora, não serve:
Registrar("Venda");
void Registrar(string texto, DateTime quando = DateTime.Now)
{
Console.WriteLine($"{quando}: {texto}");
}
$ dotnet run opcconst.cs /home/ana/cs/opcconst.cs(3,48): error CS1736: Valor do parâmetro padrão "quando" deve ser uma constante de tempo de compilação Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O truque de sempre: use null como padrão e troque pelo valor de verdade dentro do método. O DateTime? é um DateTime que aceita null, e o ?? quer dizer "se for null, use este outro" (aulas null e Tipos Anuláveis e Operadores ??, ?. e ??=). Para a saída não mudar a cada execução, o exemplo usa uma data fixa no lugar de DateTime.Now:
Registrar("Venda", new DateTime(2026, 3, 14, 9, 30, 0));
Registrar("Troca");
void Registrar(string texto, DateTime? quando = null)
{
DateTime data = quando ?? new DateTime(2026, 1, 1);
Console.WriteLine($"{data:dd/MM/yyyy HH:mm} {texto}");
}
$ dotnet run opcnull.cs 14/03/2026 09:30 Venda 01/01/2026 00:00 Troca
No programa de verdade, a linha seria DateTime data = quando ?? DateTime.Now;, e o padrão seria "agora".
3. Argumentos nomeados
Na chamada, você pode escrever o nome do parâmetro, dois-pontos e o valor. Os quatro jeitos abaixo fazem a mesma coisa:
MostrarItem("Caneta", 3m);
MostrarItem(produto: "Caneta", preco: 3m);
MostrarItem(preco: 3m, produto: "Caneta");
MostrarItem("Caneta", preco: 3m);
void MostrarItem(string produto, decimal preco)
{
Console.WriteLine($"{produto}: {preco:C}");
}
$ dotnet run nomeados.cs Caneta: R$ 3,00 Caneta: R$ 3,00 Caneta: R$ 3,00 Caneta: R$ 3,00
- produto: "Caneta"
- "O valor
"Caneta"vai para o parâmetroproduto". O nome tem de ser exatamente o da declaração, e os dois-pontos o separam do valor. - MostrarItem(preco: 3m, produto: "Caneta")
- Com nomes, a ordem não importa: quem decide é o nome, e não a posição.
- MostrarItem("Caneta", preco: 3m)
- Dá para misturar: os primeiros por posição, os seguintes por nome.
Pular um opcional do meio
Aqui está a grande vantagem de juntar os dois recursos. Com nome, você manda só o opcional que quer e deixa os outros no padrão:
MostrarItem("Borracha", 1.75m, descontoPorcento: 20);
MostrarItem("Borracha", 1.75m, quantidade: 4);
void MostrarItem(string produto, decimal preco, int quantidade = 1, int descontoPorcento = 0)
{
decimal total = preco * quantidade * (100 - descontoPorcento) / 100;
Console.WriteLine($"{quantidade} x {produto} (-{descontoPorcento}%) = {total:C}");
}
$ dotnet run pular.cs 1 x Borracha (-20%) = R$ 1,40 4 x Borracha (-0%) = R$ 7,00
Na primeira chamada, o desconto foi passado, e a quantidade ficou no padrão (1). Só com posição, isso seria impossível: não existe "deixar um buraco" na chamada.
$ dotnet run pularpos.cs « MostrarItem("Borracha", 1.75m, , 10);
/home/ana/cs/pularpos.cs(1,32): error CS0839: Argumento ausente
Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
Nomes deixam true e false legíveis
Leia a primeira chamada abaixo sem olhar o método. O que quer dizer true, false?
FazerPedido("Caderno", true, false);
FazerPedido("Caderno", embrulhar: true, entregar: false);
void FazerPedido(string produto, bool embrulhar, bool entregar)
{
Console.WriteLine($"{produto}: embrulhar={embrulhar}, entregar={entregar}");
}
$ dotnet run bools.cs Caderno: embrulhar=True, entregar=False Caderno: embrulhar=True, entregar=False
As duas fazem o mesmo, mas só a segunda se explica sozinha. Sem o nome, quem lê precisa abrir o método para descobrir; e, se trocar a ordem dos dois bool, o compilador não percebe (os dois são do mesmo tipo). Regra do guia: quando passar true, false ou um número "mágico", use o nome.
Também vale para os métodos do .NET
Os parâmetros dos métodos da biblioteca também têm nomes, e muitos têm opcionais. O Split de um texto, por exemplo, tem um segundo parâmetro opcional, options, que pode pedir para jogar fora os pedaços vazios:
Console.WriteLine(Math.Round(2.675m, 2));
Console.WriteLine(Math.Round(2.675m, decimals: 1));
string texto = "maçã;banana;;uva";
Console.WriteLine(texto.Split(';').Length);
Console.WriteLine(texto.Split(';', StringSplitOptions.RemoveEmptyEntries).Length);
$ dotnet run biblio.cs 2,68 2,7 4 « "maçã", "banana", "" e "uva" 3 « o pedaço vazio entre ;; foi descartado
No editor, ao digitar o ( de uma chamada, a lista de parâmetros aparece com os nomes e os valores padrão (veja Editores e IDEs). É de lá que você tira o nome para um argumento nomeado, como o decimals do Math.Round.
Formas diferentes
Na forma com classe, nada muda:
class Program
{
static void Main()
{
Saudar("Ana");
Saudar(nome: "Bruno", saudacao: "Boa noite");
}
static void Saudar(string nome, string saudacao = "Olá")
{
Console.WriteLine($"{saudacao}, {nome}!");
}
}
$ dotnet run classeopc.cs Olá, Ana! Boa noite, Bruno!
Existe outro jeito de ter "o mesmo método com menos argumentos": escrever duas versões dele, cada uma com uma lista de parâmetros. Isso é a sobrecarga, a aula seguinte à de params.
Qual usar?
| Situação | Use |
|---|---|
| O parâmetro tem um valor que serve na maioria das chamadas | Opcional: int quantidade = 1 |
| Quer mandar só um dos opcionais | Argumento nomeado: descontoPorcento: 20 |
A chamada tem true/false ou vários números do mesmo tipo | Argumentos nomeados, para a leitura |
| O padrão depende de algo que só se sabe rodando (a hora, um arquivo) | Padrão null + ?? dentro do método |
| As versões curta e longa fazem coisas bem diferentes | Sobrecarga |
Quando dá errado
error CS1737: Os parâmetros opcionais devem aparecer após todos os parâmetros necessários
O que significa: há um parâmetro com = antes de um sem =.
Como consertar: leve os opcionais para o fim da lista. Se as chamadas existentes quebrarem, use argumentos nomeados nelas.
error CS1736: Valor do parâmetro padrão "quando" deve ser uma constante de tempo de compilação
O que significa: o padrão é algo calculado com o programa rodando: DateTime.Now, new List<int>(), uma variável.
Como consertar: use null (ou default) como padrão e calcule o valor de verdade dentro do método, com ??.
error CS1739: A melhor sobrecarga de "MostrarItem" não tem um parâmetro chamado "valor"
$ dotnet run nomeerr.cs /home/ana/cs/nomeerr.cs(1,23): error CS1739: A melhor sobrecarga de "MostrarItem" não tem um parâmetro chamado "valor" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: a chamada foi MostrarItem("Caneta", valor: 3m), mas o parâmetro se chama preco. O nome tem de ser exatamente o da declaração, com maiúsculas e minúsculas.
Como consertar: preco: 3m. Se você renomear um parâmetro, as chamadas com nome também precisam mudar.
error CS8323: O argumento nomeado 'preco' é usado fora de posição, mas é seguido por um argumento sem nome
$ dotnet run nomepos.cs /home/ana/cs/nomepos.cs(1,13): error CS8323: O argumento nomeado 'preco' é usado fora de posição, mas é seguido por um argumento sem nome Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: MostrarItem(preco: 3m, "Caneta"). O preco nomeado foi para a primeira posição (que é do produto), e depois veio um valor sem nome: o compilador não tem como saber para onde vai o "Caneta".
Como consertar: depois que um argumento sai da posição dele, todos os seguintes precisam de nome: MostrarItem(preco: 3m, produto: "Caneta"). Ou volte à ordem normal.
error CS1744: O argumento nomeado "produto" especifica um parâmetro para o qual já foi atribuído um argumento posicional
$ dotnet run nomedup.cs /home/ana/cs/nomedup.cs(1,23): error CS1744: O argumento nomeado "produto" especifica um parâmetro para o qual já foi atribuído um argumento posicional Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: MostrarItem("Caneta", produto: "Lápis"). O "Caneta", por posição, já foi para produto; o nome tentou mandar outro valor para o mesmo parâmetro. E o preco ficou sem nada.
Como consertar: cada parâmetro recebe um valor só. Aqui, o certo era MostrarItem("Lápis", preco: 3m). Com o mesmo nome escrito duas vezes (preco: 3m, preco: 4m), o erro é o CS1740: O argumento nomeado "preco" não pode ser especificado várias vezes.
error CS0839: Argumento ausente
O que significa: há uma vírgula seguida de outra, um "buraco" na chamada, tentando pular um opcional.
Como consertar: tire o buraco e passe o opcional seguinte pelo nome: MostrarItem("Borracha", 1.75m, descontoPorcento: 10).
Referência rápida
| Escrita | O que faz |
|---|---|
void M(string a, int b = 1) | b é opcional; se a chamada não mandar, vale 1 |
M("x") / M("x", 5) | Sem o opcional (padrão) / com ele |
M(b: 5, a: "x") | Argumentos nomeados, em qualquer ordem |
M("x", c: 2) | Pula o opcional b e manda só c |
DateTime? quando = null + quando ?? DateTime.Now | Padrão calculado na hora de rodar |
| Padrões aceitos | Números, textos, true/false, null, default, constantes |
Exercícios
1. Valor padrão
Complete a declaração para que moeda valha "R$" quando a chamada não mandar nada:
2. Argumento nomeado
O método é void Agendar(string cliente, bool urgente = false). Complete a chamada que agenda a Bruna com urgência, usando o nome do parâmetro:
3. Quanto vale?
Com int Somar(int a, int b = 10, int c = 100) => a + b + c;, quanto vale Somar(1, c: 5)?
4. Qual declaração compila?
5. Com void M(string produto, decimal preco), qual chamada não compila?
6. Por que escrever Enviar(texto, urgente: true) em vez de Enviar(texto, true)?
Missão final: o formulário de pedido
Missão: as quatro chamadas já estão prontas. Escreva o método FazerPedido com quatro parâmetros, nesta ordem: produto (string, obrigatório), quantidade (int, padrão 1), embrulhar e entregar (os dois bool, padrão false). Ele mostra uma linha no formato abaixo, com sim ou não (dica: embrulhar ? "sim" : "não", de Operador Ternário). A saída deve ser:
1 x Caderno | embrulho: não | entrega: não 3 x Caneta | embrulho: não | entrega: não 1 x Mochila | embrulho: sim | entrega: não 2 x Agenda | embrulho: não | entrega: sim
FazerPedido("Caderno");
FazerPedido("Caneta", 3);
FazerPedido("Mochila", embrulhar: true);
FazerPedido("Agenda", 2, entregar: true);
// escreva o método FazerPedido aqui
FazerPedido("Caderno");
FazerPedido("Caneta", 3);
FazerPedido("Mochila", embrulhar: true);
FazerPedido("Agenda", 2, entregar: true);
void FazerPedido(string produto, int quantidade = 1, bool embrulhar = false, bool entregar = false)
{
string embrulho = embrulhar ? "sim" : "não";
string entrega = entregar ? "sim" : "não";
Console.WriteLine($"{quantidade} x {produto} | embrulho: {embrulho} | entrega: {entrega}");
}
Se apareceu CS1739, o nome de um parâmetro não bate com o da chamada (embrulhar e entregar, sem acento). Se apareceu CS7036, falta um valor padrão.
Resumo
- Um parâmetro com
= valoré opcional: se a chamada não mandar nada, ele recebe o padrão. - Opcionais vêm depois de todos os obrigatórios (
CS1737), e o padrão tem de ser uma constante (CS1736). Para "agora" ou valores calculados, usenulle??. - Um argumento nomeado (
preco: 3m) escolhe o parâmetro pelo nome: a ordem deixa de importar, e dá para pular opcionais do meio. - Depois de um nomeado fora da posição, os seguintes também precisam de nome (
CS8323). Nome errado dáCS1739; parâmetro preenchido duas vezes,CS1744ouCS1740. - Use nomes quando a chamada tiver
true/falseou números que não se explicam sozinhos.