Descompilei algumas bibliotecas C # 7 e vi ValueTuple
genéricos sendo usados. O que são ValueTuples
e por que não Tuple
?
139
Descompilei algumas bibliotecas C # 7 e vi ValueTuple
genéricos sendo usados. O que são ValueTuples
e por que não Tuple
?
Respostas:
A
ValueTuple
é uma estrutura que reflete uma tupla, igual àSystem.Tuple
classe original .A principal diferença entre
Tuple
eValueTuple
são:System.ValueTuple
é um tipo de valor (struct), enquantoSystem.Tuple
é um tipo de referência (class
). Isso é significativo quando se fala de alocações e pressão do GC.System.ValueTuple
não é apenas umstruct
, é um mutável , e é preciso ter cuidado ao usá-los como tal. Pense no que acontece quando uma classe possuiSystem.ValueTuple
um campo.System.ValueTuple
expõe seus itens por meio de campos em vez de propriedades.Até o C # 7, o uso de tuplas não era muito conveniente. Seus nomes de campo são
Item1
,Item2
etc, e a linguagem não havia fornecido açúcar de sintaxe para eles, como a maioria das outras linguagens (Python, Scala).Quando a equipe de design da linguagem .NET decidiu incorporar tuplas e adicionar açúcar de sintaxe a elas no nível da linguagem, um fator importante foi o desempenho. Por
ValueTuple
ser um tipo de valor, você pode evitar a pressão do GC ao usá-los porque (como um detalhe de implementação) eles serão alocados na pilha.Além disso, um
struct
obtém semântica de igualdade automática (superficial) pelo tempo de execução, enquanto umclass
não. Embora a equipe de design tenha certeza de que haverá uma igualdade ainda mais otimizada para as tuplas, a empresa implementou uma igualdade personalizada para ela.Aqui está um parágrafo das notas de design de
Tuples
:Exemplos:
Você pode ver facilmente que o trabalho
System.Tuple
se torna ambíguo muito rapidamente. Por exemplo, digamos que temos um método que calcula uma soma e uma contagem de aList<Int>
:No final do recebimento, acabamos com:
A maneira como você pode desconstruir as tuplas de valor em argumentos nomeados é o poder real do recurso:
E no lado receptor:
Ou:
Presentes do compilador:
Se olharmos para a capa do exemplo anterior, podemos ver exatamente como o compilador está interpretando
ValueTuple
quando pedimos que ele desconstrua:Internamente, o código compilado utiliza
Item1
eItem2
, mas tudo isso é abstraído de nós, pois trabalhamos com uma tupla decomposta. Uma tupla com argumentos nomeados é anotada com oTupleElementNamesAttribute
. Se usarmos uma única variável nova em vez de decompor, obteremos:Note que o compilador ainda tem que fazer alguma mágica acontecer (por meio do atributo) quando depurar nossa aplicação, como seria estranho ver
Item1
,Item2
.fonte
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
A diferença entre
Tuple
eValueTuple
é queTuple
é um tipo de referência eValueTuple
é um tipo de valor. O último é desejável, pois as alterações no idioma no C # 7 usam tuplas com muito mais freqüência, mas alocar um novo objeto no heap para cada tupla é uma preocupação de desempenho, principalmente quando é desnecessário.No entanto, no C # 7, a idéia é que você nunca precise usar explicitamente nenhum desses tipos, porque o açúcar da sintaxe foi adicionado para o uso da tupla. Por exemplo, em C # 6, se você quiser usar uma tupla para retornar um valor, faça o seguinte:
No entanto, no C # 7, você pode usar isso:
Você pode dar um passo adiante e fornecer os nomes dos valores:
... Ou desconstrua totalmente a tupla:
As tuplas não eram frequentemente usadas no C # pré-7 porque eram complicadas e detalhadas, e apenas realmente usadas nos casos em que a construção de uma classe / estrutura de dados para apenas uma única instância do trabalho seria mais problemática do que valeria a pena. Mas no C # 7, as tuplas agora têm suporte no nível do idioma, portanto, usá-las é muito mais limpa e mais útil.
fonte
Eu olhei para a fonte de ambos
Tuple
eValueTuple
. A diferença é queTuple
é umclass
eValueTuple
é umstruct
que implementaIEquatable
.Isso significa que
Tuple == Tuple
retornaráfalse
se não forem a mesma instância, masValueTuple == ValueTuple
retornarátrue
se forem do mesmo tipo eEquals
retornarátrue
para cada um dos valores que eles contêm.fonte
Outras respostas se esqueceram de mencionar pontos importantes.Em vez de reformular, vou fazer referência à documentação XML do código-fonte :
Os tipos ValueTuple (da aridade de 0 a 8) compreendem a implementação do tempo de execução subjacente às tuplas em C # e as tuplas de estrutura em F #.
Além de criados através da sintaxe da linguagem , eles são criados com mais facilidade pelos
ValueTuple.Create
métodos de fábrica. OsSystem.ValueTuple
tipos diferem dosSystem.Tuple
tipos em que:Com a introdução desse tipo e do compilador C # 7.0, você pode escrever facilmente
E retorne dois valores de um método:
Ao contrário de
System.Tuple
você pode atualizar seus membros (Mutable) porque são campos públicos de leitura e gravação que podem receber nomes significativos:fonte
class MyNonGenericType : MyGenericType<string, ValueTuple, int>
etc.Além dos comentários acima, um problema infeliz do ValueTuple é que, como um tipo de valor, os argumentos nomeados são apagados quando compilados no IL, para que não estejam disponíveis para serialização no tempo de execução.
Seus argumentos nomeados ainda serão finalizados como "Item1", "Item2" etc., quando serializados via, por exemplo, Json.NET.
fonte
Participação tardia para adicionar um esclarecimento rápido sobre esses dois factóides:
Alguém poderia pensar que mudar em massa as tuplas de valor seria simples:
Alguém pode tentar solucionar isso assim:
A razão para esse comportamento peculiar é que as tuplas de valor são exatamente baseadas em valor (estruturas) e, portanto, a chamada .Select (...) funciona em estruturas clonadas e não nos originais. Para resolver isso, devemos recorrer a:
Como alternativa, é claro que se pode tentar a abordagem direta:
Espero que isso ajude alguém que luta para fazer cara de coroa fora das tuplas de valor hospedadas na lista.
fonte