J'ai décompilé des bibliothèques C # 7 et j'ai vu ValueTuple
génériques utilisés. Que sont ValueTuples
et pourquoi pas Tuple
place?
Réponses
Trop de publicités?Quelles sont
ValuedTuples
et, pourquoi pas,Tuple
à la place?Un
ValueTuple
est une structure qui reflète un tuple, même que l'original,System.Tuple
classe.La principale différence entre
Tuple
etValueTuple
sont:
System.ValueTuple
est une valeur de type (struct), tandis que l'System.Tuple
est un type de référence (class
). C'est important lorsque l'on parle des allocations et des GC de la pression.System.ValueTuple
n'est pas seulement unstruct
, c'est une mutable un, et un seul doit être prudent lors de leur utilisation en tant que tels. Pensez à ce qui arrive quand une classe est titulaire d'unSystem.ValueTuple
comme un champ.System.ValueTuple
expose ses articles via les champs plutôt que des propriétés.Jusqu'à ce que C# 7, à l'aide des n-uplets n'était pas très commode. Les noms de champ sont
Item1
,Item2
, etc, et la langue n'avait pas fourni la syntaxe de sucre pour eux comme la plupart des autres langages (Python, Scala).Lorsque l' .NET de la langue de l'équipe de conception a décidé d'intégrer les tuples et ajouter la syntaxe de sucre au niveau de langue un facteur important a été la performance. Avec
ValueTuple
étant d'un type de valeur, vous pouvez éviter de GC de la pression lors de l'utilisation car (comme un détail d'implémentation), ils vont être alloués sur la pile.En outre, un
struct
obtient automatique (peu de) l'égalité sémantique par le moteur d'exécution, lorsqu'unclass
ne le fait pas. Bien que l'équipe de conception à fait sûr qu'il y aura encore plus optimisée de l'égalité pour les n-uplets, ainsi mis en œuvre une coutume de l'égalité.Voici un paragraphe de la conception des notes de
Tuples
:Struct ou Class:
Comme mentionné, je propose de faire un tuple types
structs
plutôt que declasses
, de sorte que l'attribution de pénalité est associé avec eux. Ils doit être aussi léger que possible.Sans doute,
structs
peut être plus coûteux, parce que l'affectation des copies d'une plus grande valeur. Donc, si ils sont affectés à une beaucoup plus que ce qu'ils sont créés, alorsstructs
serait un mauvais choix.Dans leur motivation, cependant, les tuples sont éphémères. Vous utilisez lorsque les pièces sont plus important que tout. Donc la commune modèle serait de construire, de retour et immédiatement déconstruire . Dans cette situation, les structures sont nettement préférable.
Les structures ont également un certain nombre d'autres avantages, qui deviendra évident dans la suite.
Exemples:
Vous pouvez facilement voir que le travail avec des
System.Tuple
devient ambigu très rapidement. Par exemple, disons que nous avons une méthode qui calcule une somme et un nombre deList<Int>
:public Tuple<int, int> DoStuff(IEnumerable<int> ints) { var sum = 0; var count = 0; foreach (var value in values) { sum += value; count++; } return new Tuple(sum, count); }
Sur l'extrémité de réception, nous nous retrouvons avec:
Tuple<int, int> result = DoStuff(Enumerable.Range(0, 10)); // What is Item1 and what is Item2? // Which one is the sum and which is the count? Console.WriteLine(result.Item1); Console.WriteLine(result.Item2);
La façon dont vous pouvez démonter la valeur de n-uplets dans les arguments nommés est la puissance réelle de la fonction:
public (int sum, int count) DoStuff(IEnumerable<int> values) { var res = (sum: 0, count: 0); foreach (var value in values) { res.sum += value; res.count++; } return res; }
Et sur la fin de réception:
var result = DoStuff(Enumerable.Range(0, 10)); Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}");
Ou:
var (sum, count) = DoStuff(Enumerable.Range(0, 10)); Console.WriteLine($"Sum: {sum}, Count: {count}");
Compilateur goodies:
Si l'on regarde sous le capot de notre exemple précédent, nous pouvons voir exactement comment le compilateur interprète
ValueTuple
lorsque nous demandons à déconstruire:[return: TupleElementNames(new string[] { "sum", "count" })] public ValueTuple<int, int> DoStuff(IEnumerable<int> values) { ValueTuple<int, int> result; result..ctor(0, 0); foreach (int current in values) { result.Item1 += current; result.Item2++; } return result; } public void Foo() { ValueTuple<int, int> expr_0E = this.DoStuff(Enumerable.Range(0, 10)); int item = expr_0E.Item1; int arg_1A_0 = expr_0E.Item2; }
En interne, le code compilé utilise
Item1
etItem2
, mais tout cela est abstrait, loin de nous depuis que nous travaillons avec une décomposé tuple. Un tuple avec des arguments nommés obtient annoté avec l'TupleElementNamesAttribute
. Si nous utilisons un seul frais de la variable au lieu de la décomposition, on obtient:public void Foo() { ValueTuple<int, int> valueTuple = this.DoStuff(Enumerable.Range(0, 10)); Console.WriteLine(string.Format("Sum: {0}, Count: {1})", valueTuple.Item1, valueTuple.Item2)); }
Notez que le compilateur a encore à faire de la magie se produire (par l'intermédiaire de l'attribut) lorsque nous debug notre application, il serait étrange de voir
Item1
,Item2
.
La différence entre Tuple
et ValueTuple
que Tuple
est un type de référence et d' ValueTuple
est un type valeur. Ce dernier est souhaitable parce que les changements de la langue en C# 7 ont tuples être utilisé beaucoup plus fréquemment, mais l'attribution d'un nouvel objet sur le tas pour chaque n-uplet est un souci de performances, en particulier lorsque c'est inutile.
Toutefois, en C# 7, l'idée est que vous ne jamais avoir à utiliser de manière explicite, soit de type cause de la syntaxe de sucre ajouté pour tuple utilisation. Par exemple, en C# 6, si vous souhaitez utiliser un tuple de retourner une valeur, vous devez effectuer les opérations suivantes:
public Tuple<string, int> GetValues()
{
// ...
return new Tuple(stringVal, intVal);
}
var value = GetValues();
string s = value.Item1;
Toutefois, en C# 7, vous pouvez utiliser ceci:
public (string, int) GetValues()
{
// ...
return (stringVal, intVal);
}
var value = GetValues();
string s = value.Item1;
Vous pouvez même aller plus loin et donner les valeurs des noms:
public (string S, int I) GetValues()
{
// ...
return (stringVal, intVal);
}
var value = GetValues();
string s = value.S;
... Ou de déconstruire le tuple entièrement:
public (string S, int I) GetValues()
{
// ...
return (stringVal, intVal);
}
var (S, I) = GetValues();
string s = S;
Les Tuples n'étaient pas souvent utilisé en C# pre-7 parce qu'ils étaient lourd et verbeux, et seulement dans les cas où la construction d'une classe de données/struct pour juste une seule instance de travail serait plus d'ennuis que cela valait la peine. Mais en C# 7, les tuples ont de la langue-support de niveau maintenant, de sorte que leur utilisation est beaucoup plus propre et plus utile.
J'ai regardé la source pour les deux Tuple
et ValueTuple
. La différence est que, Tuple
est class
et ValueTuple
est struct
qui implémente IEquatable
.
Cela signifie qu' Tuple == Tuple
sera de retour false
s'ils ne sont pas dans la même instance, mais ValueTuple == ValueTuple
sera de retour true
si elles sont de même type et de même Equals
retours true
pour chacune des valeurs qu'elles contiennent.
En plus des commentaires ci-dessus, ValueTuple regrette que, comme type de valeur, les arguments nommés sont effacés lors de la compilation en IL, de sorte qu'ils ne sont pas disponibles pour la sérialisation au moment de l'exécution.
C'est-à-dire que vos arguments nommés seront toujours "Item1", "Item2", etc. lorsqu'ils seront sérialisés via Json.NET, par exemple.
D'autres réponses oublié de mentionner les points importants.Au lieu de reformuler, je vais faire référence à la documentation XML à partir de code source:
Le ValueTuple types (d'arité 0 à 8) comprennent l'exécution de la mise en œuvre que sous-tend les tuples en C# et struct tuples en F#.
Outre créé via la syntaxe de la langue, ils sont plus facilement créés via l'
ValueTuple.Create
méthodes de fabrique.
L' System.ValueTuple
types diffèrent de l' System.Tuple
types de:
- ils sont des structures plutôt que de classes,
- ils sont mutables plutôt qu'en lecture seule, et
- leurs membres (comme Élément1, Élément2, etc) sont des champs plutôt que de propriétés.
Avec l'introduction de ce type et C# 7.0 compilateur, vous pouvez facilement écrire
(int, string) idAndName = (1, "John");
Et de renvoyer deux valeurs à partir d'une méthode:
private (int, string) GetIdAndName()
{
//.....
return (id, name);
}
Contrairement aux System.Tuple
vous pouvez mettre à jour ses membres (Mutable) parce qu'ils sont publics en lecture-écriture des Champs qui peuvent être donnés des noms significatifs:
(int id, string name) idAndName = (1, "John");
idAndName.name = "New Name";