Qual requisito a tupla foi projetada para resolver?

94

Estou olhando para o novo recurso C # de tuplas. Estou curioso, qual problema a tupla foi projetada para resolver?

Para que você usou tuplas em seus aplicativos?

Atualizar

Obrigado pelas respostas até agora, deixe-me ver se entendi bem as coisas. Um bom exemplo de tupla foi apontado como coordenadas. Isso parece certo?

var coords = Tuple.Create(geoLat,geoLong);

Em seguida, use a tupla assim:

var myLatlng = new google.maps.LatLng("+ coords.Item1 + ", "+ coords.Item2 + ");

Isso é correto?

Chaddeus
fonte
4
Bem, posso pensar em duas respostas ...
Noon Silk
13
Se o conceito de "coordenada" faz sentido como uma classe, então eu faria dele uma classe. Use tuplas para aquelas situações em que há não algum conceito sensível "lógica de negócios" para o conjunto de dados que justifica fazendo um tipo.
Eric Lippert
13
As coordenadas não são um bom exemplo de tupla. Tupla em C # é apenas uma solução ad-hoc nos casos em que você precisa retornar (de uma função) dois valores. Em vez de definir um tipo de resultado mais um parâmetro de saída, é mais elegante retornar uma tupla. Dessa forma, você não precisa declarar o segundo parâmetro com antecedência. A diferença é ainda mais clara em C ++ (você pode constar o resultado, mas não pode constar do parâmetro out / ref).
greenoldman
8
Porque o python o tinha, e o python não pode ter nada que o C # não tenha;)
Evan Plaice

Respostas:

117

Ao escrever programas, é extremamente comum querer agrupar logicamente um conjunto de valores que não têm comunalidade suficiente para justificar a criação de uma classe.

Muitas linguagens de programação permitem que você agrupe logicamente um conjunto de valores não relacionados de outra forma, sem criar um tipo de apenas uma maneira:

void M(int foo, string bar, double blah)

Logicamente, isso é exatamente o mesmo que um método M que recebe um argumento que é uma 3-tupla de int, string, double. Mas espero que você não faça:

class MArguments
{
   public int Foo { get; private set; } 
   ... etc

a menos que MArguments tivesse algum outro significado na lógica de negócios.

O conceito de "agrupar um monte de dados não relacionados de outra forma em alguma estrutura que é mais leve do que uma classe" é útil em muitos, muitos lugares, não apenas para listas de parâmetros formais de métodos. É útil quando um método tem duas coisas para retornar, ou quando você deseja separar um dicionário de dois dados em vez de um, e assim por diante.

Linguagens como F #, que suportam tipos de tupla nativamente, fornecem uma grande flexibilidade para seus usuários; eles são um conjunto extremamente útil de tipos de dados. A equipe BCL decidiu trabalhar com a equipe F # para padronizar em um tipo de tupla para o framework de forma que todas as linguagens pudessem se beneficiar deles.

No entanto, neste ponto, não há suporte de linguagem para tuplas em C #. Tuplas são apenas outro tipo de dados como qualquer outra classe de estrutura; não há nada de especial neles. Estamos considerando adicionar um suporte melhor para tuplas em versões futuras hipotéticas do C #. Se alguém tiver alguma ideia sobre que tipo de recursos envolvendo tuplas você gostaria de ver, ficaria feliz em repassá-los para a equipe de design. Cenários realistas são mais convincentes do que reflexões teóricas.

Eric Lippert
fonte
1
@MalcomTucker: A psicronia e o paralelismo estão definitivamente em nossas mentes como áreas ricas onde ferramentas de linguagem podem ser usadas para resolver problemas reais. Não acho que os fluxos de trabalho assíncronos do estilo F # sejam necessariamente os mais adequados para C #, mas eles definitivamente são inspiradores.
Eric Lippert
60
Embora as tuplas sejam úteis, uma grande desvantagem é a clareza. É difícil de ler e compreender o código que se refere a Item1, Item2, etc ... Se tuplas nunca fazer alcançar o suporte ao idioma em C #, que seria maravilhoso para permitir que seus membros para ser nomeado (ou pelo menos alias) de maneira que permite que o código que usa para serem mais compreensíveis. Em um futuro hipotético, eu também adoraria ver tuplas de "forma" apropriada como parâmetros legais para métodos que usam parâmetros individuais (e vice-versa). Portanto, Tuple<int,string,bool>pode ser passado para M(int,string,bool). Isso tornaria os métodos de memorização muito mais fáceis.
LBushkin
2
Essas "tuplas" são basicamente todos tipos de publicacesso anônimos struct com membros não nomeados . Eu preferiria que o programador escrevesse um structcom membros nomeados e retornasse isso, do que ter que lidar com um tupleque não me diga nada sobre a semântica de seus membros.
bobobobo
1
@ Hi-Angel: O argumento não é discutir sobre o que conta como uma tupla e o que não é, mas sim sobre qual é o conjunto de recursos que os desenvolvedores modernos de linha de negócios poderiam usar para aumentar sua produtividade . LBushkin está expressando um pedido de recurso que a equipe de design ouve o tempo todo: o desejo de fazer um "tipo de disco" de algum tipo sem o problema e as despesas de fazer uma classe inteira apenas para reunir alguns campos. C # já tem esse recurso para métodos; é chamada de "lista de parâmetros".
Eric Lippert
2
@ Hi-Angel: Você provavelmente não faria o argumento "se quiser acessar os parâmetros do seu método pelo nome, então você deve fazer uma classe separada" porque isso parece extremamente pesado, mas só parece pesado porque estamos acostumados a ter o capacidade de vincular instantaneamente um conjunto de variáveis ​​arbitrárias a tipos e nomes arbitrários ao chamar um método . Imagine que nenhuma linguagem tivesse esse recurso; cada método só poderia ter um único argumento, e se você quisesse passar dois, teria que fazer um tipo e passar uma instância dele.
Eric Lippert
22

Tuplas fornecem uma implementação imutável de uma coleção

Além dos usos comuns de tuplas:

  • para agrupar valores comuns sem ter que criar uma classe
  • para retornar vários valores de uma função / método
  • etc ...

Objetos imutáveis ​​são inerentemente thread-safe:

Objetos imutáveis ​​podem ser úteis em aplicativos multithread. Múltiplos threads podem atuar nos dados representados por objetos imutáveis ​​sem a preocupação de os dados serem alterados por outros threads. Objetos imutáveis ​​são, portanto, considerados mais seguros para threads do que objetos mutáveis.

De "Immutable Object" na wikipedia

Evan Solha
fonte
Obrigado por adicionar informações adicionais à pergunta. Muito apreciado.
Chaddeus
Se você quiser uma coleção imutável, deve usar uma coleção imutável, não uma tupla. A diferença é que para a maioria das coleções imutáveis, você não precisa especificar o número de elementos em tempo de compilação
BlueRaja - Danny Pflughoeft
@ BlueRaja-DannyPflughoeft Quando escrevi esta resposta, as classes de coleção imutáveis ​​C # ainda não estavam disponíveis no BCL. Acho que vou mudar 'coleção' para 'objeto', já que o MS castrou a implementação de Tupla em C #
Evan Solha
12

Ele fornece uma alternativa para refou outse você tiver um método que precise retornar vários novos objetos como parte de sua resposta.

Ele também permite que você use um tipo embutido como um tipo de retorno se tudo o que você precisa fazer é mash-up dois ou três tipos existentes e não deseja ter que adicionar uma classe / estrutura apenas para esta combinação. (Você já desejou que uma função pudesse retornar um tipo anônimo? Esta é uma resposta parcial para essa situação.)

Toby
fonte
Droga. Agradável. Gostaria de ter verificado quais são as tuplas há alguns anos;).
sabiland
10

Geralmente é útil ter um tipo de "par", usado apenas em situações rápidas (como retornar dois valores de um método). Tuplas são uma parte central de linguagens funcionais como F #, e C # as escolheu ao longo do caminho.

Stephen Cleary
fonte
System.Web.UI tem uma classe Pair msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx .
StingyJack
5

muito útil para retornar dois valores de uma função

Keith Nicholas
fonte
1
Talvez para embrulhar um tipo anônimo?
bloparod
1
Eu as vejo como estruturas anônimas
Matt Evans
4

Pessoalmente, acho que Tuplas é uma parte iterativa do desenvolvimento quando você está em um ciclo investigativo ou apenas "brincando". Como uma tupla é genérica, tendo a pensar nela quando trabalho com parâmetros genéricos - especialmente quando desejo desenvolver uma parte genérica de código e estou começando no final do código, em vez de me perguntar "como gostaria desta chamada olhar?".

Freqüentemente, percebo que a coleção formada pela Tupla se torna parte de uma lista, e olhar para Lista> não expressa realmente a intenção da lista, ou como ela funciona. Muitas vezes "vivo" com isso, mas me vejo querendo manipular a lista e alterar um valor - nesse ponto, não quero necessariamente criar uma nova Tupla para isso, portanto, preciso criar minha própria classe ou estrutura para segurá-lo, para que eu possa adicionar código de manipulação.

Obviamente, sempre há métodos de extensão - mas frequentemente você não deseja estender esse código extra para implementações genéricas.

Houve ocasiões em que quis expressar dados como tuplas, mas não tive tuplas disponíveis. (VS2008), caso em que acabei de criar minha própria classe Tuple - e não a tornei thread-safe (imutável).

Portanto, acho que sou da opinião de que as tuplas são preguiçosas em programar à custa de perder um nome de tipo que descreva seu propósito. A outra despesa é que você tem que declarar a assinatura da Tupla onde quer que seja usada como parâmetro. Depois de vários métodos que começam a parecer inchados, você pode sentir como eu, que vale a pena fazer uma classe, pois ela limpa as assinaturas dos métodos.

Eu tendo a começar tendo a classe como um membro público da classe em que você já está trabalhando. Mas no momento em que se estende além de simplesmente uma coleção de valores, ele obtém seu próprio arquivo e eu o removo da classe que o contém.

Então, em retrospecto, acredito que uso tuplas quando não quero sair e escrever uma aula, e só quero pensar sobre o que estou escrevendo agora. O que significa que a assinatura da Tupla pode mudar bastante no texto meia hora enquanto eu descubro quais dados vou precisar para este método, e como ele está retornando os valores que retornará.

Se eu tiver a chance de refatorar o código, geralmente questionarei o lugar de um Tuple nele.

Simon Miller
fonte
3

Questão antiga desde 2010, e agora em 2017 o Dotnet muda e se torna mais inteligente.

C # 7 apresenta suporte de linguagem para tuplas, o que permite nomes semânticos para os campos de uma tupla usando novos tipos de tupla mais eficientes.

No vs 2017 e .Net 4.7 (ou instalando o pacote nuget System.ValueTuple), você pode criar / usar uma tupla de uma maneira simples e muito eficiente:

     var person = (Id:"123", Name:"john"); //create tuble with two items
     Console.WriteLine($"{person.Id} name:{person.Name}") //access its fields

Retornando mais de um valor de um método:

    public (double sum, double average) ComputeSumAndAverage(List<double> list)
    {
       var sum= list.Sum();
        var average = sum/list.Count;
        return (sum, average);
    }

    How to use:

        var list=new List<double>{1,2,3};
        var result = ComputeSumAndAverage(list);
        Console.WriteLine($"Sum={result.sum} Average={result.average}");    

Para mais detalhes, leia: https://docs.microsoft.com/en-us/dotnet/csharp/tuples

M.Hassan
fonte
1

Uma tupla é freqüentemente usada para retornar vários valores de funções quando você não deseja criar um tipo específico. Se você está familiarizado com Python, Python já teve isso por muito tempo.

Randy Minder
fonte
1

Retornando mais de um valor de uma função. getCoordinates () não é muito útil se retornar apenas x ou y ou z, mas fazer uma classe e um objeto completos para conter três ints também parece muito pesado.

Dean J
fonte
1

Um uso comum pode ser evitar a criação de classes / estruturas que contenham apenas 2 campos, em vez de criar uma Tupla (ou um KeyValuePair por enquanto). Útil como valor de retorno, evite passar N parâmetros ...

user347594
fonte
0

Acho o KeyValuePair refrescante em C # para iterar os pares de valores-chave em um Dicionário.

Jubal
fonte
KeyValuePairpode ser visto como uma solução alternativa para fins especiais. Em Python, iterar sobre dictapenas retorna tuplas (chave, valor).
dan04
Sim, como python. :-) Eu não sabia que KeyValuePair em c # não era uma tupla?
Jubal
0

É realmente útil ao retornar valores de funções. Podemos ter vários valores de volta e isso é uma grande proteção em alguns cenários.

Akshat
fonte
0

Me deparei com esse benchmark de desempenho entre tuplas e pares de valores-chave e provavelmente você o achará interessante. Resumindo diz que Tuple tem vantagem por ser uma classe, pois é armazenada no heap e não na pilha e quando passado como argumento seu ponteiro é a única coisa que vai. Mas KeyValuePair é um structs, por isso é mais rápido de alocar, mas é mais lento quando usado.

http://www.dotnetperls.com/tuple-keyvaluepair

Ognyan Dimitrov
fonte