CodeGym /Cursos /C# SELF /Modificadores de parâmetro ( ...

Modificadores de parâmetro ( ref, out, in)

C# SELF
Nível 12 , Lição 4
Disponível

1. Introdução

Em C#, por padrão, todos os parâmetros dos métodos são passados por valor, ou seja, o método recebe uma cópia, não o original.

Mas e se você quiser mudar uma variável externa de dentro do método? Ou retornar vários valores de um método? Ou, quem sabe, você precisa passar uma struct grandona pra dentro do método, mas não quer copiar esse "trambolho", só passar a referência pra economizar memória?

Tudo isso é sobre os modificadores de parâmetro. Eles te dão o controle sobre o "caminho de passagem" dos dados entre o código chamador e o método.

Tipos de modificadores

Modificador Passagem de dados Precisa inicializar antes de chamar? Pode ler dentro? Pode escrever dentro? Cenários típicos de uso
ref Nos dois sentidos Sim Sim Sim Passar variável pra ler e/ou alterar
out Só pra fora Não Não (antes de atribuir) Sim (obrigatório!) Retornar vários valores de um método
in Só pra dentro Sim Sim Não Passar structs grandes pra "só leitura" economizando memória

Relaxa, já já a gente passa por cada um deles. É bem mais simples do que parece.

2. Modificador ref: Passando por referência: dá pra ler e escrever

Imagina assim: você leva uma xícara de café pro seu amigo e fala — "Tá aqui, pode beber, esquentar, até colocar açúcar se quiser". O amigo pode fazer o que quiser com o café, e o que sobrar (ou não sobrar) volta pra você. É assim que funciona o ref.


void DoSomething(ref int x)
{
    x = x + 10; // Alterando o parâmetro de entrada
}
        
Método com parâmetro ref

Pra passar um parâmetro como ref, tem que declarar com esse modificador tanto no método quanto na chamada:

int meuNumero = 5;
DoSomething(ref meuNumero);
Console.WriteLine(meuNumero); // vai mostrar 15

Tem que ser uma variável, não dá pra passar uma expressão por referência. Esse código não vai funcionar:

DoSomething(ref 10);		// aqui tem que ser uma variável!
  • Antes da chamada: a variável tem que estar inicializada (tem que ter valor).
  • Dentro do método: pode ler e mudar o valor.
  • Depois da chamada: as mudanças ficam salvas.

Exemplo: Trocando valores de variáveis

Às vezes você precisa trocar os valores de duas variáveis. Mas tem um detalhe: em C#, tipos de valor (tipo int) por padrão são passados por valor. Ou seja, quando você passa eles pra um método, só copia o valor, não a referência. Pra que o método consiga mudar as variáveis do código chamador, tem que usar ref.

Exemplo sem ref — não funciona:

// Tentando trocar valores — não vai funcionar
void Swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 10;
int y = 20;
Swap(x, y); // Passa cópias dos valores
Console.WriteLine($"{x}, {y}"); // → 10, 20 — nada mudou!

Dentro da função Swap as variáveis a e b são cópias de x e y. Você só muda as cópias, os originais ficam de boa.

Exemplo com ref — as variáveis trocam de lugar


// Trocando valores usando ref
void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 10;
int y = 20;
Swap(ref x, ref y); // Passa referências das variáveis
Console.WriteLine($"{x}, {y}"); // → 20, 10 — trocou certinho
        

ref deixa você passar a referência da variável, não só o valor.
Por isso a função Swap mexe direto em x e y, mudando o conteúdo delas.

Quando usar ref?

  • Quando precisa mudar a variável passada (tipo aumentar, trocar).
  • Não precisa retornar um novo valor (como com return), porque quer mudar o argumento direto.
  • Não use pra "despejar" um monte de valores — pra isso tem outro modificador melhor.

3. Modificador out: Passando pra fora

Agora imagina: você chega no amigo, mas sua xícara tá vazia, e pede: "Enche aí — pode ser café, chá, o que quiser!" Depois disso, seu amigo TEM que encher, senão o compilador vai reclamar.

out serve pra um método devolver vários valores, sem usar o valor de retorno. Esses parâmetros têm que ser inicializados dentro do método.


void GetCoordinates(out int x, out int y)
{
    x = 5;
    y = 10; // Sem isso - erro!
}
        
Método com parâmetros out
  • Antes da chamada: não precisa inicializar a variável, pode até escrever out int x direto na chamada.
  • Dentro do método: tem que atribuir valor antes de sair do método, senão o compilador faz greve.
  • Depois da chamada: a variável tem o novo valor.

Exemplo: Retornando dois valores de um método

void ParseNameAndAge(string input, out string nome, out int idade)
{
    string[] partes = input.Split(',');	// transforma a string em array de strings - divide por ','
    nome = partes[0];
    idade = int.Parse(partes[1]);
}

string entradaUsuario = "Ivan,25";
ParseNameAndAge(entradaUsuario, out string nomeUsuario, out int idadeUsuario);
Console.WriteLine($"{nomeUsuario} — {idadeUsuario} anos");

Quando usar out?

  • Quando precisa retornar vários valores do método (tipo dividir uma string em partes, igual acima).
  • Quando o tipo do valor de retorno não é fixo (tipo tentar converter string pra número).

Exemplo do .NET: O método int.TryParse(string, out int) — clássico!

string entrada = "123";
if (int.TryParse(entrada, out int numeroConvertido))
{
    Console.WriteLine($"Convertido: {numeroConvertido}");
}
else
{
    Console.WriteLine("Erro na conversão!");
}

O método int.TryParse retorna true se conseguiu converter a string pra número e false se não conseguiu. O valor numérico mesmo ele retorna pelo out.

Erros comuns e situações engraçadas usando ref e out

  • Esqueceu de inicializar a variável pra ref — aí o compilador fica bravo.
  • Não atribuiu valor pra out — o compilador fica mais bravo ainda.
  • Esqueceu de colocar o modificador na chamada: escreveu DoSomething(x) em vez de DoSomething(ref x).
  • Usar ref ou out com constantes ou literais não pode! Só pode ser variável. Só elas têm endereço na memória pra formar referência.

4. Modificador in: Só leitura, e só por referência

Às vezes você precisa passar um objeto grande e complicado pra um método. Normalmente esse objeto é passado por referência, mas aí o método pode acabar mudando ele sem querer — afinal, você deu acesso total.

Teoricamente, dava pra fazer uma cópia completa do objeto e passar ela. Assim o original não muda. Mas se o objeto for muito grande, você vai copiar um monte de dados à toa. Muito melhor passar o objeto pro método e pedir pro compilador garantir que ninguém vai mexer nele.

in — é um modificador novo que deixa passar structs por referência, mas só pra leitura. É tipo "espada rara na vitrine": pode olhar, mas não pode pegar.


void PrintPoint(in Point pt)
{
    pt.X = 5; // Erro! Só leitura.
    Console.WriteLine($"Ponto: {pt.X}, {pt.Y}");  //Assim pode
}
        
Método com parâmetro in (só leitura)
  • Antes da chamada: a variável tem que estar inicializada.
  • Dentro do método: só pode ler, não pode mudar.
  • Economia de memória: a struct não é copiada, é passada por referência.

Serve, por exemplo, pra arrays de structs grandes, pra evitar cópia desnecessária. Mas com classes (tipos de referência) in não faz diferença.

Exemplo: Passando struct grande por referência pra não copiar

struct BigData
{
    public int A, B, C, D, E;
}

void PrintBigData(in BigData data)
{
    data.A = 10; // não pode - só leitura!
    Console.WriteLine(data.A + data.B + data.C + data.D + data.E);
}

BigData meusDados = new BigData { A = 10, B = 20, C = 30, D = 40, E = 50 };
PrintBigData(in meusDados);

5. Perguntas que sempre aparecem:

Posso usar ref/out/in com arrays e tipos de referência?
Pode sim! Mas lembra: arrays já são tipos de referência. Passando array com ref, você pode trocar a referência do array. Normalmente isso é mais útil pra structs.

Qual a diferença entre ref e out, se posso pegar valor nos dois?
Com ref a variável tem que estar inicializada antes de usar. Com out não precisa, mas dentro do método ela TEM que receber valor pelo menos uma vez.

E se eu tentar usar literal (tipo número 5) com ref/out/in?
O compilador já dá erro na hora. Só variável!

Quantos parâmetros posso fazer ref/out/in?
Quantos quiser, sem limite! Só não exagera pra não ferrar a leitura do código: se tiver mais de dois ou três, pensa em retornar uma tupla, struct ou classe.

1
Pesquisa/teste
Classes e objetos, nível 12, lição 4
Indisponível
Classes e objetos
Introdução à herança
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION