Zdekompilowałem niektóre biblioteki C # 7 i zobaczyłem ValueTuple
używane typy generyczne. Czym są ValueTuples
i dlaczego nie Tuple
?
139
Zdekompilowałem niektóre biblioteki C # 7 i zobaczyłem ValueTuple
używane typy generyczne. Czym są ValueTuples
i dlaczego nie Tuple
?
Odpowiedzi:
A
ValueTuple
jest strukturą, która odzwierciedla krotkę, taką samą jak oryginałSystem.Tuple
klasa.Główne różnice między
Tuple
iValueTuple
to:System.ValueTuple
jest typem wartości (struct), podczas gdySystem.Tuple
jest typem referencyjnym (class
). Ma to znaczenie, gdy mówimy o alokacjach i presji na GC.System.ValueTuple
to nie tylko astruct
, jest zmienna i należy zachować ostrożność, używając ich jako takich. Pomyśl, co się dzieje, gdy klasa maSystem.ValueTuple
jako pole.System.ValueTuple
ujawnia swoje elementy za pośrednictwem pól zamiast właściwości.Aż do C # 7 używanie krotek nie było zbyt wygodne. Ich nazwy pól to
Item1
,Item2
itd., A język nie dostarczył im cukru składniowego, jak robi to większość innych języków (Python, Scala).Kiedy zespół projektantów języka .NET zdecydował się włączyć krotki i dodać do nich cukier składniowy na poziomie języka, ważnym czynnikiem była wydajność. Z
ValueTuple
bycia typ wartości, można uniknąć presji GC podczas korzystania z nich, ponieważ (jak szczegółowo wdrożenia) będą przydzielane na stosie.Ponadto a
struct
otrzymuje automatyczną (płytką) semantykę równości przez środowisko wykonawcze, gdzie aclass
nie. Chociaż zespół projektowy upewnił się, że będzie jeszcze bardziej zoptymalizowana równość dla krotek, dlatego zaimplementował dla niej niestandardową równość.Oto akapit z uwag projektowych
Tuples
:Przykłady:
Możesz łatwo zauważyć, że praca z
System.Tuple
bardzo szybko staje się niejednoznaczna. Na przykład, powiedzmy, że mamy metodę, która oblicza sumę i liczbęList<Int>
:Po stronie odbiorczej otrzymujemy:
Sposób, w jaki można dekonstruować krotki wartości na nazwane argumenty, to prawdziwa siła tej funkcji:
A po stronie odbiorczej:
Lub:
Dodatki kompilatora:
Jeśli spojrzymy pod okładkę naszego poprzedniego przykładu, możemy dokładnie zobaczyć, jak kompilator interpretuje,
ValueTuple
kiedy poprosimy go o dekonstrukcję:Wewnętrznie skompilowany kod wykorzystuje
Item1
iItem2
, ale wszystko to jest od nas oderwane, ponieważ pracujemy na zdekomponowanej krotce. Krotka z nazwanymi argumentami zostanie oznaczona rozszerzeniemTupleElementNamesAttribute
. Jeśli zamiast dekompozycji użyjemy jednej świeżej zmiennej, otrzymamy:Zwróć uwagę, że kompilator nadal musi wykonać jakąś magię (za pośrednictwem atrybutu) podczas debugowania naszej aplikacji, ponieważ byłoby dziwne zobaczyć
Item1
, żeItem2
.źródło
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Różnica między
Tuple
iValueTuple
polega na tym, żeTuple
jest to typ referencyjny iValueTuple
typ wartości. To drugie jest pożądane, ponieważ zmiany języka w C # 7 powodują, że krotki są używane znacznie częściej, ale przydzielanie nowego obiektu na stercie dla każdej krotki jest problemem związanym z wydajnością, szczególnie gdy jest to niepotrzebne.Jednak w C # 7 chodzi o to, że nigdy nie musisz jawnie używać żadnego typu z powodu dodawania cukru składniowego do użycia krotki. Na przykład w języku C # 6, jeśli chcesz użyć krotki do zwrócenia wartości, musisz wykonać następujące czynności:
Jednak w C # 7 możesz użyć tego:
Możesz nawet pójść o krok dalej i nadać wartościom nazwy:
... Lub całkowicie zdekonstruuj krotkę:
Krotki nie były często używane w języku C # przed wersją 7, ponieważ były uciążliwe i rozwlekłe, a tak naprawdę używane tylko w przypadkach, gdy tworzenie klasy / struktury danych dla pojedynczego wystąpienia pracy byłoby bardziej kłopotliwe niż było warte. Ale w C # 7 krotki mają teraz obsługę na poziomie języka, więc ich używanie jest znacznie czystsze i bardziej przydatne.
źródło
Spojrzałem na źródło zarówno
Tuple
iValueTuple
. Różnica polega na tym, żeTuple
jest to aclass
iValueTuple
jest tostruct
implementacjaIEquatable
.Oznacza to, że
Tuple == Tuple
zwróci,false
jeśli nie są one tą samą instancją, aleValueTuple == ValueTuple
zwróci,true
jeśli są tego samego typu iEquals
zwrócitrue
dla każdej z wartości, które zawierają.źródło
W innych odpowiedziach zapomniałem wspomnieć o ważnych kwestiach, zamiast przeformułować odwołam się do dokumentacji XML z kodu źródłowego :
Typy ValueTuple (od arity 0 do 8) obejmują implementację środowiska uruchomieniowego, która opiera się na krotkach w C # i strukturach w języku F #.
Oprócz tworzenia za pomocą składni języka , najłatwiej jest je tworzyć za pomocą
ValueTuple.Create
metod fabrycznych. TeSystem.ValueTuple
typy różnią się odSystem.Tuple
rodzaju tym, żeDzięki wprowadzeniu tego typu i kompilatorowi C # 7.0 można łatwo pisać
I zwróć dwie wartości z metody:
W przeciwieństwie do
System.Tuple
ciebie możesz aktualizować jego członków (Mutable), ponieważ są to publiczne pola do odczytu i zapisu, którym można nadać znaczące nazwy:źródło
class MyNonGenericType : MyGenericType<string, ValueTuple, int>
itp.Oprócz powyższych komentarzy, jedną niefortunną wadą ValueTuple jest to, że jako typ wartości, nazwane argumenty są usuwane podczas kompilacji do IL, więc nie są dostępne do serializacji w czasie wykonywania.
tzn. Twoje słodkie nazwane argumenty nadal będą kończyć się jako „Pozycja1”, „Pozycja2” itd. po serializacji przez np. Json.NET.
źródło
Późne dołączanie, aby dodać szybkie wyjaśnienie tych dwóch faktów:
Można by pomyśleć, że masowa zmiana krotek wartości byłaby prosta:
Ktoś może spróbować obejść to w następujący sposób:
Przyczyną tego dziwacznego zachowania jest to, że krotki wartości są dokładnie oparte na wartościach (struktury), a zatem wywołanie .Select (...) działa raczej na sklonowanych strukturach niż na oryginałach. Aby rozwiązać ten problem, musimy skorzystać z:
Alternatywnie można oczywiście spróbować prostego podejścia:
Mam nadzieję, że pomoże to komuś, kto zmaga się z krotkami wartości przechowywanymi na liście.
źródło