Esse é o primeiro post da série sobre C# 7. Pra acompanhar a série você pode seguir a tag C#7 no blog ou voltar no post que agrega a série.

Vou começar falando de Tuplas, uma das novas funcionalidades que mais auxilia a deixar o código mais limpo. Está bastante aceito que a funcionalidade vai entrar no C# 7, mas parece que alguns detalhes ainda não foram fechados. Você pode ler o issue no github que sugere tuplas no repositório do Roslyn, é o issue chamado “Proposal: Language support for Tuples”, e a funcionalidade é acompanhada por uma tag chamada “New Language Feature – Tuples” que agrega sugestões em volta da proposta principal.

O que é uma tupla

A tupla permite agregar valores, é pra isso que ela serve. O caso mais comum é pra permitir múltiplos valores de retorno de uma função sem precisar de parâmetros “out”. Mas também teremos literais de tuplas, permitindo criar uma variável tipada como uma tupla.

Um método que retorna uma tupla ficaria assim:

public (int soma, int quantidade) Agrupar(IEnumerable valores) { ... }

Note que não é utilizado o tipo “Tuple” que já existe na Base Class Library.

A chamada do método é normal, o interessante é como o retorno é utilizado. Aqui ele seria utilizado passando a tupla para uma variável:

var t = Agrupar(meusValores);
Console.WriteLine($"Soma: {t.soma}, Quantidade: {t.quantidade}");

Provavelmente haverá um tipo na BCL que será utilizado para criar a tupla, e ele será uma struct. Nada será alterado no runtime, após a compilação o tipo da tupla já existente será construído e retornado. O nome das propriedades também deverá sumir, ou seja, “soma” e “quantidade” nunca vão parar na IL gerada. Seria possível, inclusive, utilizar os nomes originais dos valores, “Item1” e “Item2”, mas isso será desencorajado (o Roslyn não mostraria essas opções no autocomplete). Mas esse código seria válido:

var t = Agrupar(meusValores);
Console.WriteLine($"Soma: {t.Item1}, Total: {t.Item2}");

Desconstruindo uma tupla

Note que o objetivo da tupla é que ela não apareça, assim, passar o retorno do método Agrupar pra uma variável faz pouco sentido, sendo muito melhor já atribuir nas variáveis que você vai utilizar, diretamente. Isso será possível, e ficará assim:

(var soma, var quantidade) = Agrupar(meusValores); // desconstruindo a tupla
Console.WriteLine($"Soma: {soma}, Quantidade: {quantidade}");

Sem dúvida alguma essa seria a forma mais comum de uso.

Eu gostaria ainda que eu pudesse desconsiderar os valores que não quero usar, podendo fazer algo desse tipo:

public (int soma, int media, int quantidade, int max) Agrupar(IEnumerable valores) { ... }
(var soma, , var quantidade) = Agrupar(meusValores);

Nesse caso, apenas soma e quantidade me interessam, já que não utilizarei media deixei o espaço vazio, e já que não utilizarei max, sequer inseri a vírgula que demarcaria seu espaço na tupla de retorno.

Outro ponto interessante seria a descontrução em variáveis já existente. Espero que essa ideia prossiga. Algo assim:

int soma, quantidade;
(soma, quantidade) = Agrupar(meusValores);

Notem a ausência do var na tupla.

Literal de tupla

Será possível declarar um literal de tupla, assim:

var res = (soma: 0, quantidade: 0);

Ainda assim, a utilidade de uma variável declarada dessa forma é discutível. O único lugar que vejo fazer sentido é para montar o retorno de uma função, dessa forma:

public (int soma, int quantidade) Agrupar(IEnumerable valores) 
{
    var res = (soma: 0, quantidade: 0);
    foreach (var v in valores) { res.soma += v; res.quantidade++; }
    return res;
}

Ainda assim, essa idéia é problemática, já que faria mais sentido simplesmente declarar as variáveis e retorná-las no final. Ainda assim, nesse caso não teríamos alocações extras, já que estamos falando de structs, e esse tipo de estrutura costuma ter overhead zero, não demandando um espaço na memória (o endereço da estrutura seria o mesmo endereço do seu primeiro valor).

Outros detalhes

Com a abordagem de um tipo interno pra represeentar as tuplas (diferente do tipo Tuple atual, que é uma classe, não uma struct) seria possível passar tuplas entre assemblies, algo fundamental nesse cenário. Há discussão de evoluir a ideia para tipos anônimos também, o que é muito interessante.

Está em discussão também a utilização das variáveis que compõe a tupla da mesma forma que usamos out parameters, ou seja, elas já vem declaradas. Achei a ideia interessante.

Outro ponto que eu gostaria de ver, e não vi mencionado, seria utilizar métodos que usam out parameters, e em vez de passar os valores como argumentos out, receber uma tupla. Isso permitiria trabalhar com todos os métodos que já temos hoje que utilizam out parameters. Por exemplo, int.TryParse, seria usado assim:

(sucesso, inteiro) = int.TryParse("1");

Isso sem alterar o método TryParse, que continuaria com seu out parameter.

Outro ponto importante seria fazer o splatting, que é quando passamos uma tupla pra uma função que tenha valores semelhantes. Assim:

void Escreve(int soma, int quantidade) => WriteLine($"Soma: {soma}, Quantidade: {quantidade}");
var res = Agrupar(meusValores);
Escreve(res);
//ou direto:
Escreve(Agrupar(meusValores));

Resumindo

A tupla é uma ideia interessantíssima que já tem suporte na sintaxe de outras linguagens, e agora teremos também no C#. Esse é um excelente exemplo de como o C# é hoje uma das linguagens estáticas mais expressivas da Microsoft, quiçá entre todas as outras.
O que vocês acharam das tuplas? Vão limpar nosso código dos out params e ajudar bastante, não? É uma das minhas funcionalidades novas preferidas. Dêem a opinião de vocês aqui nos comentários, e se tiverem sugestões, escrevam lá no issue do Github, com certeza os core commiters vão querer saber.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.