174 votes

Quelle est la différence entre System.ValueTuple et System.Tuple?

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?

256voto

Yuval Itzchakov Points 13820

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 et ValueTuple 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 un struct, 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'un System.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'un class 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 de classes, 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, alors structs 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 de List<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 et Item2, 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.

31voto

Abion47 Points 6101

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.

14voto

Peter Morris Points 2402

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.

8voto

ZenSquirrel Points 31

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.

6voto

user3185569 Points 22924

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";

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X