Motivação

O retorno de múltiplos valores a partir de um método é um pequeno desafio para muitos desenvolvedores iniciantes, independentemente da linguagem de programação. No C#, isso não é diferente. Em geral, essa é uma necessidade muito requisitada no dia-a-dia. Um método de paginação de dados, por exemplo, precisa retornar o número de páginas e os próprios dados paginados; um outro método pode retornar o status e a data da última conexão ao servidor.

Esse artigo visa preencher essa lacuna, mostrando as opções e porque algumas delas são menos recomendadas que outras, levando em consideração as boas práticas de desenvolvimento.

saiba mais Saiba mais sobre a linguagem de programação C#

Opção 1: Múltiplos valores com parâmetro out

A primeira forma de retorno de múltiplos valores é através de parâmetros out. Esses parâmetros são, até certo ponto, simples de serem utilizados, mas possuem algumas limitações. Além disso, o código não fica muito claro quando fazemos uso desses parâmetros.

A grande limitação do retorno de valores com o parâmetro out está relacionada à declaração de tais parâmetros em métodos assíncronos, o que não é permitido pelo C#. Essa restrição acontece devido à natureza desse tipo de parâmetro: ele espera uma saída do método, de forma “imediata”, síncrona, algo que os métodos assíncronos não podem fornecer.

Para entendermos melhor como lidar com esses parâmetros, vejamos a Listagem 1.


  01 public string RetornarMensagemOut(out bool flag)
  02 {
  03     flag = true;
  04     return "Mensagem";
  05 }
  
Listagem 1. Método para retornar mensagem com flag utilizando parâmetro out.

Linha 1: declaração do método RetornarMensagemOut(out). Note que temos um valor de retorno (string) e um parâmetro out booleano, necessário para “retornar” mais um valor a partir do método. Note, ainda, que isso gera certa complicação em termos de legibilidade de código;

Linha 3: a variável flag, que é um parâmetro de saída do método (out), é setada. Isso é necessário no caso de parâmetros out. Saiba que um erro de compilação acontecerá caso as variáveis out não sejam setadas antes do “fim” do método;

Linha 4: o retorno tradicional (string) é realizado, devolvendo uma mensagem a partir do método.

Com isso, podemos perceber que, embora seja uma opção válida e o código funcione (apenas para métodos síncronos!), acaba gerando alguma complicação.

Como um exemplo, o código abaixo demonstra como chamar o método apresentado na Listagem 1:


  bool outFlag;
  string mensagem = RetornarMensagemOut(out outFlag);
  

Observe que precisamos de uma variável (“outFlag”) para receber o valor do parâmetro out. Assim, é possível identificar, também, que o uso desse tipo de parâmetro não é tão simples como parece, especialmente se precisamos retornar mais do que dois valores.

Opção 2: Múltiplos valores com classe POCO

Outra opção para o retorno de múltiplos valores a partir de um método é a criação de uma classe POCO (Plain Old Class Object). Esse tipo de classe tem como objetivo simplesmente guardar dados, ou seja, não possuem nenhum comportamento definido. Assim, no caso de nosso exemplo, no qual queremos retornar uma mensagem e uma flag indicando o estado, teríamos uma classe como a mostrada na Listagem 2.


  01 public class MensagemComFlag
  02 {
  03     public string Mensagem { get; set; }
  04     public bool Flag { get; set; }
  05 }
  
Listagem 2. Classe POCO para retorno de múltiplos valores.

Linha 1: declaração da classe MensagemComFlag;

Linha 3: declaração da propriedade Mensagem (string);

Linha 4: declaração da propriedade Flag (bool).

Podemos constatar que essa classe nos permite resolver um dos principais problemas que tínhamos com os parâmetros out: a impossibilidade de utilização com métodos assíncronos. Ademais, a extensão para mais de dois valores de retorno acaba se tornando mais simples: basta adicionar outras propriedades à classe POCO. Isso pode ser confirmado na Listagem 3, que mostra como o método se torna simples ao adotarmos essa segunda opção.


  01 public MensagemComFlag RetornarMensagemPOCO()
  02 {
  03     return new MensagemComFlag() { Mensagem = "Mensagem", Flag = true };
  04 }
  
Listagem 3. Método para retornar mensagem com flag utilizando classe POCO

Linha 1: declaração do método RetornarMensagemPOCO(). Note que essa declaração é mais simples do que a que tínhamos com parâmetros out. Há apenas um valor de retorno, utilizando a classe POCO criada;

Linha 3: retorna um novo objeto do tipo MensagemComFlag, com os mesmos dados declarados no parâmetro out. Embora seja mais simples, é interessante observar que essa chamada cria um objeto para que seja possível passar os valores para o usuário a partir do método.

O ponto negativo dessa abordagem é o overhead de código criado. Imagine que haja 100 métodos com múltiplos valores de retorno. Que impacto teria a criação de 100 classes POCO para retornar esses valores? Certamente algo proibitivo para o desenvolvimento e manutenção.

No entanto, as vantagens que a adoção de classes POCO traz para o retorno de múltiplos valores não podem ser desprezadas. A ideia de centralizar os valores de retorno em um só lugar é muito interessante pela simplificação mantida no código, contudo, o .NET fornece, ainda, uma melhor opção de atingirmos esse objetivo.

Opção 3: Múltiplos valores com Tuples

Matematicamente, um Tuple é uma lista finita de elementos ordenados. No C#, esse conceito foi trabalhado de uma forma ligeiramente diferente, criando-se uma classe genérica, que nada mais é do que uma coleção de dados de diferentes tipos (tipos esses que são passados em tempo de compilação).

O conceito de generics no .NET é empregado para criar uma classe que pode ser utilizada para passar quantos valores quisermos, da forma que desejarmos. Nesse caso, em específico, criaremos um objeto do tipo Tuple. Diante disso, temos uma abordagem muito similar à que vimos com a classe POCO.

A consequência disso é que mantemos os pontos positivos da abordagem anterior, como a simplicidade do código do método e a possibilidade de uso com métodos assíncronos. Contudo, além disso, ainda evitamos o overhead da criação de classes POCO para cada retorno de múltiplos valores necessário.

A Listagem 4 mostra em detalhes como ficaria esse código.


  01 public Tuple<string, bool> RetornarMensagemTuple()
  02 {
  03     return new Tuple<string, bool>("Mensagem", true);
  04 }
  
Listagem 4. Método para retornar mensagens com flag utilizando Tuple.

Linha 1: declaração do método RetornarMensagemTuple(). Repare que a assinatura é similar à que vimos na abordagem com a classe POCO MensagemComFlag. No entanto, a classe Tuple é genérica e aceita quantos valores desejarmos;

Linha 3: retorna um novo objeto do tipo Tuple, com parâmetros string e bool. Observe que ainda trata-se de um novo objeto, mas com a diferença de não haver overhead no código para a criação de uma classe específica para o retorno de um simples método.

Após conhecer as opções, é importante entender que, em alguns casos, pode ser mais interessante, em termos de desempenho da aplicação, optar por um parâmetro out, ao invés da classe Tuple, especialmente se tivermos apenas dois valores a serem retornados. Mesmo assim, do ponto de vista da legibilidade do código, a prioridade é pela adoção da classe Tuple, empregando os parâmetros genéricos T para quantos dados precisarmos retornar.