Retornando dois valores, Tuple vs 'out' vs 'struct'
-
28-10-2019 - |
Pergunta
Considere uma função que retorna dois valores.Podemos escrever:
// Using out:
string MyFunction(string input, out int count)
// Using Tuple class:
Tuple<string, int> MyFunction(string input)
// Using struct:
MyStruct MyFunction(string input)
Qual é a prática recomendada e por quê?
Solução
Cada um deles tem seus prós e contras.
Os parâmetros de saída são rápidos e baratos, mas requerem que você passe uma variável e dependa da mutação.É quase impossível usar corretamente um parâmetro out com LINQ.
As tuplas exercem pressão sobre a coleção e não são autodocumentadas."Item1" não é muito descritivo.
Estruturas personalizadas podem demorar para copiar se forem grandes, mas são autodocumentáveis e eficientes se forem pequenas.No entanto, também é difícil definir um monte de estruturas personalizadas para usos triviais.
Eu estaria inclinado para a solução de estrutura personalizada, todas as outras coisas sendo iguais.Ainda melhor, porém, é fazer uma função que retorne apenas um valor .Por que você está retornando dois valores em primeiro lugar?
ATUALIZAÇÃO: observe que as tuplas em C # 7, enviadas seis anos após a redação deste artigo, são tipos de valor e, portanto, menos propensos a criar pressão de coleção.
Outras dicas
Acho que a resposta depende da semântica do que a função está fazendo e da relação entre os dois valores.
Por exemplo, os métodos TryParse
usam um parâmetro out
para aceitar o valor analisado e retornam um bool
para indicar se a análise foi bem-sucedida ou não.Os dois valores realmente não pertencem um ao outro, portanto, semanticamente, faz mais sentido, e a intenção do código é mais fácil de ler, usar o parâmetro out
.
Se, no entanto, sua função retornar coordenadas X / Y de algum objeto na tela, os dois valores semanticamente pertencem um ao outro e seria melhor usar um struct
.
Eu pessoalmente evitaria usar um tuple
para qualquer coisa que fosse visível para o código externo devido à sintaxe estranha para recuperar os membros.
Somando-se às respostas anteriores, C # 7 traz tuplas de tipo de valor, ao contrário do System.Tuple
que é um tipo de referência e também oferece semântica aprimorada.
Você ainda pode deixá-los sem nome e usar a sintaxe .Item*
:
(string, string, int) getPerson()
{
return ("John", "Doe", 42);
}
var person = getPerson();
person.Item1; //John
person.Item2; //Doe
person.Item3; //42
Mas o que é realmente poderoso sobre esse novo recurso é a capacidade de ter tuplas nomeadas.Portanto, poderíamos reescrever o acima assim:
(string FirstName, string LastName, int Age) getPerson()
{
return ("John", "Doe", 42);
}
var person = getPerson();
person.FirstName; //John
person.LastName; //Doe
person.Age; //42
A desestruturação também é suportada:
(string firstName, string lastName, int age) = getPerson()
Vou seguir com a abordagem de usar o parâmetro Out porque na segunda abordagem você exigiria criar um objeto da classe Tuple e então agregar valor a ele, o que eu acho uma operação cara em comparação com retornar o valor do parâmetro out.No entanto, se você quiser retornar vários valores na classe Tupla (o que não pode ser conseguido apenas retornando um parâmetro de saída), irei para a segunda abordagem.
Você não mencionou mais uma opção, que é ter uma classe personalizada em vez de struct.Se os dados têm semântica associada a eles que pode ser operada por funções, ou o tamanho da instância é grande o suficiente (> 16 bytes como regra prática), uma classe personalizada pode ser preferida. O uso de "out" não é recomendado na API pública por causa de sua associação a ponteiros e requer compreensão de como funcionam os tipos de referência.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Tuple é bom para uso interno, mas o uso é estranho na API pública. Então, meu voto é entre struct e classe para API pública.
Não existe uma "prática recomendada".É com o que você se sente confortável e o que funciona melhor em sua situação.Contanto que você seja consistente com isso, não há problema com nenhuma das soluções que você postou.