Introdução a Tuplas em C# em 10 Minutos ou Menos
[[academy-video-youtube({"vid": "QC6hpl2iU0c", "start_time": "0", "title": "Intro to Tuples in C# In 10 Minutes or Less", "creator": "Tim Corey", "length": "9m 49s"})]]
Métodos C# são projetados em torno de um único valor de retorno, o que funciona até você se deparar com um caso onde um retorno realmente não é suficiente. A validação de endereço é o exemplo clássico: você precisa saber se o input é válido e, se for, deseja receber de volta a forma canônica limpa. Embrulhar esse par em uma nova classe parece pesado para um método, e parâmetros 'out' misturam input e output de uma forma que envelhece mal. Tuplas foram adicionadas à linguagem para que esta situação exata tenha uma resposta mais clara.
Em seu vídeo "Intro to Tuples in C# In 10 Minutes or Less", Tim Corey atravessa tuplas construindo um pequeno método de validação de endereço que precisa retornar tanto uma string formatada quanto um indicador de validez. Ao longo do caminho, ele cobre a sintaxe para retornar uma tupla, por que nomear os membros importa, como dividir o resultado em variáveis separadas no local da chamada e como ignorar partes que você não se importa usando o caractere descarte. Leitores que ainda buscam classes de resultado únicas ou parâmetros out encontrarão um padrão mais leve aqui.
O Problema: Retornar Mais de Um Valor
[0:18 - 2:50] Tim começa com um método ValidateAddress que aceita uma string de endereço. O comportamento que ele deseja é simples: se o input corresponder a "123 Sesame St", retornar o "123 Sesame Street" formatado corretamente; caso contrário, sinalizar que o endereço é inválido. Duas peças de informação precisam voltar de uma única chamada, a string limpa e a bandeira de sucesso.
As opções pré-tupla são ambas estranhas. Criar uma classe ValidateAddressResult funciona, mas deixa um tipo de propósito único no projeto que nenhum outro código jamais instanciará. Usar um parâmetro 'out' para enviar o endereço formatado de volta pela lista de parâmetros também funciona, mas mistura input e output na assinatura do método e torna o local da chamada mais difícil de ler. Tuplas foram introduzidas para lidar com este caso sem pagar nenhum dos custos.
Retornando uma Tupla Sem Nome
[2:50 - 4:01] A sintaxe mínima para um tipo de retorno de tupla envolve envolver os dois tipos entre parênteses:
public (string, bool) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
public (string, bool) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
O método agora retorna um par (string, bool). O inconveniente aparece imediatamente quando algo tenta usá-lo. Sem nomes atribuídos aos membros, o IntelliSense os expõe como Item1 e Item2, o que não informa nada ao próximo leitor sobre o que cada valor representa. Tim marca isso como a razão pela qual ele nunca envia tuples sem nome, mesmo que a sintaxe seja a mais curta. O custo de ler result.Item2 seis meses depois é maior do que o custo de digitar um nome uma vez.
Nomear os Membros da Tupla
[4:01 - 6:30] Atribuir nomes a cada membro da tupla é uma mudança de uma linha na declaração do tipo de retorno:
public (string Address, bool IsValid) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
public (string Address, bool IsValid) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
Agora o local da chamada lê naturalmente:
var result = ValidateAddress("123 Sesame St");
if (result.IsValid)
{
Console.WriteLine($"Your validated address is {result.Address}");
}
else
{
Console.WriteLine("That is an invalid address");
}
var result = ValidateAddress("123 Sesame St");
if (result.IsValid)
{
Console.WriteLine($"Your validated address is {result.Address}");
}
else
{
Console.WriteLine("That is an invalid address");
}
Executar o código com "123 Sesame St" imprime a forma canônica. Remover o 1 inicial para produzir "23 Sesame St" faz com que IsValid retorne falso e imprima a mensagem de endereço inválido. O contrato do método agora é autodescritivo no local da chamada sem exigir um arquivo de classe de apoio para uma tupla nomeada.
Uma tupla pode carregar mais de dois membros. Três, quatro ou cinco funcionam da mesma maneira. Tim observa que uma vez que uma tupla cresce além de três ou quatro membros, um registro ou classe geralmente se torna a escolha mais clara, mas para dois ou três valores relacionados de um único método, a tupla continua a ser a opção mais leve.
Desconstruindo a Tupla em Variáveis Separadas
[6:30 - 7:39] Às vezes, quem chama prefere manter cada parte do resultado em sua própria variável em vez de acessar um único result. C# oferece suporte direto a isso com a sintaxe de desconstrução:
(string message, bool valid) = ValidateAddress("123 Sesame St");
if (valid)
{
Console.WriteLine($"Your validated address is {message}");
}
(string message, bool valid) = ValidateAddress("123 Sesame St");
if (valid)
{
Console.WriteLine($"Your validated address is {message}");
}
Os nomes das variáveis no lado esquerdo não precisam corresponder aos nomes dos membros da tupla no método. Aqui message substitui Address e valid substitui IsValid. Tim aponta que isso é um desconstrutor, não um destruidor; as duas palavras soam semelhantes, mas se referem a conceitos completamente diferentes. O deconstructor divide a tupla nas variáveis locais que o chamador realmente deseja.
Pulando Valores com o Caractere de Descarte
[7:41 - 8:54] Quando quem chama só se importa com um subconjunto dos valores retornados, o caractere de descarte (_) descarta os que não são necessários:
(string message, _) = ValidateAddress("123 Sesame St");
Console.WriteLine(message);
(string message, _) = ValidateAddress("123 Sesame St");
Console.WriteLine(message);
O sublinhado diz ao compilador "Estou ignorando este valor de propósito." Isso é mais expressivo do que atribuir o membro indesejado a um nome de variável descartável como unused, porque um descarte comunica intenção. Qualquer pessoa lendo a linha sabe que ignorar IsValid é uma escolha deliberada para este local de chamada, não um descuido.
Concluindo: Quando as Tuplas Ganham o Seu Lugar
[8:54 - 9:28] O caso de uso principal para tuplas é retornar vários valores de um método sem criar uma classe que existe apenas para ser retornada uma vez. Nomear os membros na assinatura do método, desconstrui-los no ponto de chamada e descartar os que o chamador não precisa são os três padrões que tornam as tuplas confortáveis de se conviver. Além disso, as tuplas são úteis em projeções LINQ e cálculos intermediários onde um valor emparelhado rápido é mais legível do que um tipo anônimo.
Conclusão
[9:28 - 9:49] As tuplas dão ao C# uma maneira compacta de retornar mais de um valor de um método. A forma nomeada é a versão que vale a pena usar no dia a dia, porque result.Address é legível enquanto result.Item1 não é. A deconstrução promove as partes de volta a variáveis locais quando o chamador prefere essa forma, e o caractere de descarte permite que uma única chamada pegue apenas o que precisa.
Dica de Exemplo: Quando você começar a desconstruir a mesma forma de tupla em três ou mais lugares, esse é o sinal para mudar para um registro em vez disso. As tuplas brilham quando a forma é local para um método; no momento em que a forma se torna parte do seu domínio, um tipo nomeado se paga.
Assista ao vídeo completo no Canal do YouTube dele e obtenha mais insights sobre como escrever C# moderno e expressivo na série de Treinamento de 10 Minutos.
