1. Anatomia de um método
public decimal CalcularTotal(List<decimal> precos, decimal desconto = 0)
{
decimal soma = precos.Sum();
return soma - desconto;
}
| Parte | Significado |
|---|---|
public | Modificador de acesso (capítulo 5) |
decimal | Tipo de retorno — void se não retorna nada |
CalcularTotal | Nome, convenção PascalCase em C# (diferente do camelCase do JS) |
(List<decimal> precos, decimal desconto = 0) | Parâmetros — o segundo tem valor padrão, tornando-o opcional |
2. Sobrecarga de métodos (overloading)
C# permite múltiplos métodos com o mesmo nome, desde que a lista de parâmetros seja diferente (tipo ou quantidade). O compilador escolhe qual chamar com base nos argumentos — algo que não existe em JavaScript, onde só a última função com aquele nome "vence".
public decimal CalcularTotal(List<decimal> precos) => precos.Sum();
public decimal CalcularTotal(List<decimal> precos, decimal desconto)
=> precos.Sum() - desconto;
// o compilador escolhe a versão certa pela quantidade de argumentos
CalcularTotal(listaDePrecos);
CalcularTotal(listaDePrecos, 10m);
3. Argumentos nomeados
Você já viu isso do lado do T-SQL (EXEC proc @Param = valor) — em C#
existe o equivalente exato, útil quando um método tem vários parâmetros opcionais:
void CriarCliente(string nome, string email, bool ativo = true, string? telefone = null) { ... }
// sem nomear, a ordem importa
CriarCliente("Ana", "ana@x.com", true, "11999999999");
// nomeando, a ordem não importa e a intenção fica explícita
CriarCliente(nome: "Ana", email: "ana@x.com", telefone: "11999999999");
4. ref, out e in — controlando como o parâmetro é passado
Por padrão, tipos por valor (capítulo 2) são copiados ao entrar num método — alterações dentro do método não afetam a variável original. Três modificadores mudam esse comportamento:
| Modificador | Efeito | Precisa inicializar antes de passar? |
|---|---|---|
ref | Passa por referência — alterações dentro do método refletem fora | Sim |
out | Passa por referência, mas o método é obrigado a atribuir um valor | Não |
in | Passa por referência só para evitar cópia (performance), mas o método não pode alterar | Sim |
void Dobrar(ref int numero) => numero *= 2;
int valor = 5;
Dobrar(ref valor);
Console.WriteLine(valor); // 10
bool TentarDividir(int a, int b, out int resultado)
{
if (b == 0) { resultado = 0; return false; }
resultado = a / b;
return true;
}
if (TentarDividir(10, 2, out int quociente))
Console.WriteLine(quociente); // 5
out em C# é conceitualmente o mesmo papel de um parâmetro
OUTPUT em uma Stored Procedure — o método/procedure "devolve" um
valor por um canal que não é o retorno principal. TryParse, que você
viu no capítulo 2, é exatamente esse padrão: bool de sucesso +
out com o valor.
5. params — quantidade variável de argumentos
decimal Somar(params decimal[] valores) => valores.Sum();
Somar(10, 20, 30); // chama com quantos argumentos quiser
Somar(); // também válido — soma 0
6. Métodos com corpo de expressão e funções locais
// expression-bodied member — equivalente a uma arrow function de uma linha
decimal Dobro(decimal x) => x * 2;
// função local — só existe dentro do método que a declara
decimal CalcularComDesconto(decimal preco, decimal percentual)
{
decimal AplicarDesconto(decimal valor) => valor - (valor * percentual / 100);
return AplicarDesconto(preco);
}
Uma função local é o parente mais próximo de uma função aninhada em JavaScript — existe só dentro do escopo do método que a contém, útil para uma lógica auxiliar pequena que não merece virar um método separado da classe.
7. Onde isso te leva
Com métodos dominados — inclusive como controlar se um parâmetro é copiado ou compartilhado — o próximo passo é organizar métodos e dados juntos dentro de classes: o início de orientação a objetos.