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.

Você vai aprender
  • 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, false numa chamada pedem um nome.
  • Os erros CS1737, CS1736, CS1739, CS8323, CS1744, CS1740 e CS0839.

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árioEm C#Nome técnico
Campo que já vem preenchido a lápisint quantidade = 1 na declaraçãoparâmetro opcional, com valor padrão
Escrever "Embrulhar: sim", dizendo o campoembrulhar: true na chamadaargumento 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 para saudacao, 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. saudacao recebe 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
ChamadaquantidadedescontoPorcento
MostrarItem("Caderno", 12.50m)1 (padrão)0 (padrão)
MostrarItem("Caneta", 3m, 4)40 (padrão)
MostrarItem("Borracha", 1.75m, 2, 10)210

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âmetro produto". 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çãoUse
O parâmetro tem um valor que serve na maioria das chamadasOpcional: int quantidade = 1
Quer mandar só um dos opcionaisArgumento nomeado: descontoPorcento: 20
A chamada tem true/false ou vários números do mesmo tipoArgumentos 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 diferentesSobrecarga

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

EscritaO 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.NowPadrão calculado na hora de rodar
Padrões aceitosNú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:

void MostrarPreco(decimal valor, string moeda )

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:

Agendar("Bruna", );

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, use null e ??.
  • 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, CS1744 ou CS1740.
  • Use nomes quando a chamada tiver true/false ou números que não se explicam sozinhos.