J'ai décompilé certaines bibliothèques C # 7 et vu des ValueTuple
génériques utilisés. Que sont ValueTuples
et pourquoi pas à la Tuple
place?
139
J'ai décompilé certaines bibliothèques C # 7 et vu des ValueTuple
génériques utilisés. Que sont ValueTuples
et pourquoi pas à la Tuple
place?
Réponses:
A
ValueTuple
est une structure qui reflète un tuple, identique à laSystem.Tuple
classe d' origine .La principale différence entre
Tuple
etValueTuple
sont:System.ValueTuple
est un type valeur (struct), tandis queSystem.Tuple
est un type référence (class
). C'est significatif quand on parle d'allocations et de pression du GC.System.ValueTuple
n'est pas seulement unstruct
, c'est un mutable , et il faut être prudent lorsqu'on les utilise comme tels. Pensez à ce qui se passe lorsqu'une classe détient unSystem.ValueTuple
comme champ.System.ValueTuple
expose ses éléments via des champs au lieu de propriétés.Jusqu'à C # 7, l'utilisation de tuples n'était pas très pratique. Leurs noms de champs sont
Item1
,Item2
, etc., et la langue n'a pas fourni de sucre de syntaxe pour eux comme la plupart d' autres langages (Python, Scala).Lorsque l'équipe de conception du langage .NET a décidé d'incorporer des tuples et de leur ajouter du sucre de syntaxe au niveau du langage, un facteur important était la performance. En
ValueTuple
étant un type valeur, vous pouvez éviter la pression du GC lors de leur utilisation car (en tant que détail d'implémentation) ils seront alloués sur la pile.De plus, a
struct
obtient une sémantique d'égalité automatique (superficielle) par le runtime, alors que aclass
ne le fait pas. Bien que l'équipe de conception se soit assurée qu'il y aurait une égalité encore plus optimisée pour les tuples, elle a donc implémenté une égalité personnalisée.Voici un paragraphe des notes de conception de
Tuples
:Exemples:
Vous pouvez facilement voir que travailler avec
System.Tuple
devient très rapidement ambigu. Par exemple, disons que nous avons une méthode qui calcule une somme et un compte de aList<Int>
:Du côté de la réception, nous nous retrouvons avec:
La façon dont vous pouvez déconstruire les tuples de valeur en arguments nommés est la vraie puissance de la fonctionnalité:
Et à la réception:
Ou:
Goodies du compilateur:
Si nous regardons sous le couvert de notre exemple précédent, nous pouvons voir exactement comment le compilateur interprète
ValueTuple
lorsque nous lui demandons de déconstruire:En interne, le code compilé utilise
Item1
etItem2
, mais tout cela nous est abstrait puisque nous travaillons avec un tuple décomposé. Un tuple avec des arguments nommés est annoté avec leTupleElementNamesAttribute
. Si nous utilisons une seule variable fraîche au lieu de la décomposer, nous obtenons:Notez que le compilateur doit encore faire quelque chose de magique se produit (via l'attribut) lorsque nous déboguer notre application, car il serait étrange de voir
Item1
,Item2
.la source
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
La différence entre
Tuple
etValueTuple
est qu'ilTuple
s'agit d'un type de référence et d'ValueTuple
un type valeur. Cette dernière option est souhaitable car les modifications apportées au langage en C # 7 font que les tuples sont utilisés beaucoup plus fréquemment, mais l'allocation d'un nouvel objet sur le tas pour chaque tuple est un problème de performances, en particulier lorsqu'il n'est pas nécessaire.Cependant, en C # 7, l'idée est que vous ne devez utiliser explicitement ou l' autre type à cause du sucre de syntaxe étant ajoutée pour une utilisation tuple. Par exemple, en C # 6, si vous souhaitez utiliser un tuple pour renvoyer une valeur, vous devez effectuer les opérations suivantes:
Cependant, en C # 7, vous pouvez utiliser ceci:
Vous pouvez même aller plus loin et donner des noms aux valeurs:
... Ou déconstruisez entièrement le tuple:
Les tuples n'étaient pas souvent utilisés dans C # pré-7 car ils étaient encombrants et détaillés, et seulement vraiment utilisés dans les cas où la construction d'une classe / structure de données pour une seule instance de travail serait plus problématique qu'elle n'en valait la peine. Mais en C # 7, les tuples ont maintenant un support au niveau du langage, donc leur utilisation est beaucoup plus propre et plus utile.
la source
J'ai regardé la source pour les deux
Tuple
etValueTuple
. La différence est queTuple
c'est unclass
etValueTuple
est unstruct
qui implémenteIEquatable
.Cela signifie que
Tuple == Tuple
cela retournerafalse
s'ils ne sont pas la même instance, maisValueTuple == ValueTuple
retourneratrue
s'ils sont du même type etEquals
retourneratrue
pour chacune des valeurs qu'ils contiennent.la source
D'autres réponses ont oublié de mentionner des points importants.Au lieu de reformuler, je vais faire référence à la documentation XML à partir du code source :
Les types ValueTuple (d'arity 0 à 8) comprennent l'implémentation d'exécution qui sous-tend les tuples en C # et les tuples de structure en F #.
En plus d'être créés via la syntaxe du langage , ils sont plus facilement créés via les
ValueTuple.Create
méthodes d'usine. LesSystem.ValueTuple
types diffèrent desSystem.Tuple
types en ce que:Avec l'introduction de ce type et du compilateur C # 7.0, vous pouvez facilement écrire
Et renvoyez deux valeurs d'une méthode:
Contrairement à
System.Tuple
vous, vous pouvez mettre à jour ses membres (Mutable) car ce sont des champs publics en lecture-écriture qui peuvent recevoir des noms significatifs:la source
class MyNonGenericType : MyGenericType<string, ValueTuple, int>
etc.En plus des commentaires ci-dessus, un inconvénient malheureux de ValueTuple est que, en tant que type de valeur, les arguments nommés sont effacés lors de la compilation en IL, ils ne sont donc pas disponibles pour la sérialisation au moment de l'exécution.
ie Vos arguments nommés doux finiront toujours par "Item1", "Item2", etc. lorsqu'ils seront sérialisés via par exemple Json.NET.
la source
Participation tardive pour ajouter une clarification rapide sur ces deux faits:
On pourrait penser que changer les tuples de valeur en masse serait simple:
Quelqu'un pourrait essayer de contourner ce problème comme suit:
La raison de ce comportement bizarre est que les tuples de valeur sont exactement basés sur la valeur (structs) et donc l'appel .Select (...) fonctionne sur les structures clonées plutôt que sur les originaux. Pour résoudre ce problème, nous devons recourir à:
Alternativement, bien sûr, on pourrait essayer l'approche simple:
J'espère que cela aide quelqu'un qui a du mal à faire la queue avec des tuples de valeur hébergés sur une liste.
la source