Ich habe einige C # 7-Bibliotheken dekompiliert und gesehen, dass ValueTuple
Generika verwendet werden. Was sind ValueTuples
und warum nicht Tuple
stattdessen?
138
Ich habe einige C # 7-Bibliotheken dekompiliert und gesehen, dass ValueTuple
Generika verwendet werden. Was sind ValueTuples
und warum nicht Tuple
stattdessen?
Antworten:
A
ValueTuple
ist eine Struktur, die ein Tupel widerspiegelt, genau wie die ursprünglicheSystem.Tuple
Klasse.Der Hauptunterschied zwischen
Tuple
undValueTuple
sind:System.ValueTuple
ist ein Werttyp (struct), währendSystem.Tuple
es sich um einen Referenztyp (class
) handelt. Dies ist sinnvoll, wenn es um Zuweisungen und GC-Druck geht.System.ValueTuple
ist nicht nur einestruct
, es ist eine veränderliche , und man muss vorsichtig sein, wenn man sie als solche verwendet. Überlegen Sie, was passiert, wenn eine KlasseSystem.ValueTuple
ein Feld enthält.System.ValueTuple
macht seine Elemente über Felder anstelle von Eigenschaften verfügbar.Bis C # 7 war die Verwendung von Tupeln nicht sehr praktisch. Ihre Feldnamen sind
Item1
,Item2
usw., und die Sprache noch nicht geliefert Syntax Zucker für sie wie die meisten anderen Sprachen tun (Python, Scala).Als das .NET-Sprachdesign-Team beschloss, Tupel zu integrieren und ihnen auf Sprachebene Syntaxzucker hinzuzufügen, war die Leistung ein wichtiger Faktor. Da
ValueTuple
es sich um einen Wertetyp handelt, können Sie GC-Druck vermeiden, wenn Sie sie verwenden, da sie (als Implementierungsdetail) auf dem Stapel zugewiesen werden.Zusätzlich
struct
erhält a zur Laufzeit eine automatische (flache) Gleichheitssemantik, wo aclass
dies nicht tut. Obwohl das Designteam dafür gesorgt hat, dass es eine noch optimierte Gleichheit für Tupel gibt, wurde daher eine benutzerdefinierte Gleichheit für Tupel implementiert.Hier ist ein Absatz aus den Design Notes von
Tuples
:Beispiele:
Sie können leicht erkennen, dass das Arbeiten mit
System.Tuple
sehr schnell mehrdeutig wird. Angenommen, wir haben eine Methode, die eine Summe und eine Anzahl von a berechnetList<Int>
:Am empfangenden Ende erhalten wir:
Die Art und Weise, wie Sie Wertetupel in benannte Argumente zerlegen können, ist die eigentliche Stärke der Funktion:
Und auf der Empfangsseite:
Oder:
Compiler-Goodies:
Wenn wir uns das Cover unseres vorherigen Beispiels ansehen, können wir genau sehen, wie der Compiler interpretiert,
ValueTuple
wenn wir ihn zum Dekonstruieren auffordern:Intern verwendet der kompilierte Code
Item1
undItem2
, aber all dies wird von uns abstrahiert, da wir mit einem zerlegten Tupel arbeiten. Ein Tupel mit benannten Argumenten wird mit dem versehenTupleElementNamesAttribute
. Wenn wir eine einzelne neue Variable verwenden, anstatt sie zu zerlegen, erhalten wir:Beachten Sie, dass der Compiler muss noch etwas Magie (über das Attribut) geschehen , wenn wir unsere Anwendung debuggen, wie es seltsam wäre , um zu sehen
Item1
,Item2
.quelle
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Der Unterschied zwischen
Tuple
undValueTuple
besteht darin, dassTuple
es sich um einen Referenztyp undValueTuple
einen Werttyp handelt. Letzteres ist wünschenswert, da bei Änderungen an der Sprache in C # 7 Tupel viel häufiger verwendet werden. Das Zuweisen eines neuen Objekts auf dem Heap für jedes Tupel ist jedoch ein Leistungsproblem, insbesondere wenn es nicht erforderlich ist.In C # 7 besteht die Idee jedoch darin, dass Sie keinen der beiden Typen explizit verwenden müssen , da der Syntaxzucker für die Tupelverwendung hinzugefügt wird. Wenn Sie beispielsweise in C # 6 ein Tupel verwenden möchten, um einen Wert zurückzugeben, müssen Sie Folgendes tun:
In C # 7 können Sie jedoch Folgendes verwenden:
Sie können sogar noch einen Schritt weiter gehen und den Werten Namen geben:
... oder das Tupel komplett dekonstruieren:
Tupel wurden in C # vor 7 nicht oft verwendet, da sie umständlich und ausführlich waren und nur dann wirklich verwendet wurden, wenn das Erstellen einer Datenklasse / -struktur für nur eine einzelne Arbeitsinstanz schwieriger war als es wert war. In C # 7 werden Tupel jetzt auf Sprachebene unterstützt, sodass ihre Verwendung viel sauberer und nützlicher ist.
quelle
Ich schaute auf die Quelle für beide
Tuple
undValueTuple
. Der Unterschied ist, dassTuple
es sich um einclass
undValueTuple
handeltstruct
, das implementiert wirdIEquatable
.Das heißt , die
Tuple == Tuple
zurückkehren wird ,false
wenn sie nicht dieselbe Instanz sind, aberValueTuple == ValueTuple
wird zurückkehren ,true
wenn sie vom gleichen Typ und sindEquals
Erträgetrue
für jeden der Werte , die sie enthalten.quelle
Andere Antworten haben vergessen, wichtige Punkte zu erwähnen. Anstelle einer Umformulierung werde ich auf die XML-Dokumentation aus dem Quellcode verweisen :
Die ValueTuple-Typen (von Arity 0 bis 8) umfassen die Laufzeitimplementierung, die Tupeln in C # und Strukturtupeln in F # zugrunde liegt.
Abgesehen davon , dass sie über die Sprachsyntax erstellt wurden , können sie am einfachsten über die
ValueTuple.Create
Factory-Methoden erstellt werden. DieSystem.ValueTuple
Typen unterscheiden sich von denSystem.Tuple
Typen darin:Mit der Einführung dieses Typs und des C # 7.0-Compilers können Sie problemlos schreiben
Und geben Sie zwei Werte von einer Methode zurück:
Im Gegensatz dazu können
System.Tuple
Sie seine Mitglieder aktualisieren (veränderbar), da es sich um öffentliche Lese- / Schreibfelder handelt, denen aussagekräftige Namen zugewiesen werden können:quelle
class MyNonGenericType : MyGenericType<string, ValueTuple, int>
usw.Zusätzlich zu den obigen Kommentaren besteht ein unglückliches Problem von ValueTuple darin, dass die benannten Argumente als Werttyp beim Kompilieren in IL gelöscht werden, sodass sie zur Laufzeit nicht für die Serialisierung verfügbar sind.
Das heißt, Ihre süßen Argumente werden weiterhin als "Item1", "Item2" usw. angezeigt, wenn sie über z. B. Json.NET serialisiert werden.
quelle
Später Beitritt, um eine kurze Erläuterung dieser beiden Faktoide hinzuzufügen:
Man würde denken, dass es einfach wäre, Werttupel in Massen zu ändern:
Jemand könnte versuchen, dies wie folgt zu umgehen:
Der Grund für dieses skurrile Verhalten ist, dass die Wertetupel genau wertebasiert sind (Strukturen) und der Aufruf von .Select (...) daher eher für geklonte Strukturen als für die Originale funktioniert. Um dies zu beheben, müssen wir auf Folgendes zurückgreifen:
Alternativ könnte man natürlich auch den einfachen Ansatz ausprobieren:
Ich hoffe, dies hilft jemandem, der Schwierigkeiten hat, aus von Listen gehosteten Wertetupeln Kopf an Schwanz zu machen.
quelle