CodeGym /Cursos /C# SELF /Retornando vários valores de uma função: parâmetros out e...

Retornando vários valores de uma função: parâmetros out e tuplas

C# SELF
Nível 11 , Lição 3
Disponível

1. Introdução

Imagina que a gente tem uma função que, pra um usuário passado (tipo pelo nome), precisa devolver de uma vez a idade, a data de registro e um flag de atividade. Em C e nas versões antigas do C#, era normal que o tipo de retorno fosse só um valor. Mas e quando tem que devolver mais de um? Essa dúvida fez nascer vários jeitos diferentes, cada um com seus prós e contras.
Nas aulas anteriores a gente já viu tuplas. Agora bora estudar os out-parâmetros e comparar esses dois jeitos.

Os jeitos mais populares:

  • Usar out-parâmetros.
  • Retornar algum objeto com os campos necessários (tipo anônimo ou classe customizada).
  • Retornar uma tupla (ValueTuple).

Nessa aula a gente vai olhar de perto dois desses jeitos – out-parâmetros e tuplas, e comparar eles "cara a cara", pra sacar qual é melhor pra retornar vários valores de uma função.

2. out-parâmetros: saudade do passado

Quando tu vê uma função tipo:


void GetUserInfo(string userName, out int idade, out DateTime dataRegistro, out bool estaAtivo)
{
    // aqui rola o cálculo
    idade = 42;
    dataRegistro = new DateTime(2010, 1, 1);
    estaAtivo = true;
}

dá até vontade de perguntar: isso é função ou uma mini lava-rápido? Tanta coisa saindo pra fora, e tudo fora do valor de retorno principal!

Como funciona

O código que chama tem que declarar antes as variáveis que a função vai preencher:

int idade;
DateTime dataReg;
bool estaAtivo;

GetUserInfo("Bob", out idade, out dataReg, out estaAtivo);

// Agora todas as variáveis estão preenchidas
Console.WriteLine($"{idade}, {dataReg}, {estaAtivo}");

Contras do jeito out

  • Mais difícil de ler: A assinatura do método fica gigante, e o sentido dos dados retornados nem sempre fica claro só pelo nome dos parâmetros.
  • Chato pra usar em cadeia: Não dá pra encaixar fácil esse método numa cadeia (tipo passar o resultado direto pra outro método).
  • Mexe nas variáveis passadas: O método tem que modificar variáveis já existentes; se esquecer do out — rola erro de compilação.
  • Delay pra inicializar: O compilador te obriga a declarar variáveis externas, mesmo se tu só quiser usar o resultado uma vez.
  • Leitura limitada em métodos grandes: Se tiver muito out-parâmetro, é fácil se perder na ordem e no propósito de cada um.

É aí que as tuplas começam a brilhar com aquele charme tech.

3. Tuplas — evolução do esquema

Comparando no exemplo

Com tupla:


public (int Idade, DateTime DataRegistro, bool EstaAtivo) GetUserInfo(string userName)
{
    // simulando busca no banco
    return (42, new DateTime(2010, 1, 1), true);
}

Usando:

// Pega tudo de uma vez e já dá nome pras variáveis
var (idade, dataReg, estaAtivo) = GetUserInfo("Bob");
Console.WriteLine($"{idade}, {dataReg}, {estaAtivo}");

Sem declaração extra, sem out, tudo super legível!

Tabela de comparação: out vs tupla

Critério Parâmetros Out Tuplas (ValueTuple)
Assinatura grande, out-parâmetros na lista compacta, tudo volta num "pacote"
Uso tem que declarar variáveis, depois chamar o método dá pra deconstruir na hora
Leitura fica confuso se tem muito out-parâmetro nomes dos elementos já explicam tudo
Dá pra combinar fácil? não sim, dá pra aninhar e passar pra frente

Tuplas VS. out — o que rola por baixo dos panos

Com out-parâmetros, a função na real mexe na memória fora do seu "território": simplificando, ela altera variáveis que foram criadas em outro lugar. Isso exige cuidado, porque tu pode acabar "zoando" o estado das variáveis (valeu, compilador, por obrigar a atribuir!).

Já a tupla é uma estrutura de dados que a função cria, inicializa toda e devolve pronta. Ou seja, tudo vem num pacote só, que tu não perde no caminho e nem esquece dele.

Lendo e mantendo o código

Fala sério — quando tu vai ler o código de outra pessoa depois de uma semana, tu quer ver:

(var idade, var cidade, var estaAtivo) = GetUserInfo("Anna");

e não

int idade;
string cidade;
bool estaAtivo;
GetUserInfo("Anna", out idade, out cidade, out estaAtivo);

Tuplas deixam as assinaturas dos métodos menos poluídas, e o resultado fica intuitivo de usar.

4. Situações onde tuplas são top

1. Quando precisa retornar resultado e mensagem de erro

public (bool Sucesso, string MensagemErro) TryProcess(string dados)
{
    if (string.IsNullOrEmpty(dados))
        return (false, "Sem dados");

    // processamento dos dados...
    return (true, "");
}

var (ok, erro) = TryProcess(input);
if (!ok)
    Console.WriteLine($"Erro: {erro}");

2. Pra funções que retornam vários resultados de busca

public (User? UsuarioEncontrado, int Indice) FindUserByName(User[] usuarios, string nome)
{
    for (int i = 0; i < usuarios.Length; i++)
    {
        if (usuarios[i].Name == nome)
            return (usuarios[i], i);
    }
    return (null, -1);
}

3. Pra “pares” de valores: calcular mínimo e máximo

public (int minimo, int maximo) FindMinMax(int[] numeros)
{
    int minimo = numeros[0], maximo = numeros[0];
    foreach (var n in numeros)
    {
        if (n < minimo) minimo = n;
        if (n > maximo) maximo = n;
    }
    return (minimo, maximo);
}

var (valorMin, valorMax) = FindMinMax(arr);
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION