Wraz z dodaniem klasy Tuple w .net 4, próbowałem zdecydować, czy użycie ich w moim projekcie jest złym wyborem, czy nie. Z mojego punktu widzenia krotka może być skrótem do napisania klasy wynikowej (jestem pewien, że są też inne zastosowania).
Więc to:
public class ResultType
{
public string StringValue { get; set; }
public int IntValue { get; set; }
}
public ResultType GetAClassedValue()
{
//..Do Some Stuff
ResultType result = new ResultType { StringValue = "A String", IntValue = 2 };
return result;
}
Odpowiada temu:
public Tuple<string, int> GetATupledValue()
{
//...Do Some stuff
Tuple<string, int> result = new Tuple<string, int>("A String", 2);
return result;
}
Pomijając więc możliwość, że brakuje mi sensu krotek, czy przykład z krotką jest złym wyborem projektowym? Wydaje mi się, że jest mniej bałaganu, ale nie tak samo dokumentujący się i czysty. Oznacza to, że w przypadku typu ResultType
jest później bardzo jasne, co oznacza każda część klasy, ale musisz zachować dodatkowy kod. Dzięki temu Tuple<string, int>
będziesz musiał spojrzeć w górę i dowiedzieć się, co Item
reprezentuje każdy z nich , ale piszesz i utrzymujesz mniej kodu.
Będziemy bardzo wdzięczni za wszelkie doświadczenia związane z tym wyborem.
źródło
Odpowiedzi:
Krotki są świetne, jeśli kontrolujesz ich tworzenie i używanie - możesz zachować kontekst, który jest niezbędny do ich zrozumienia.
Jednak w publicznym interfejsie API są mniej skuteczne. Konsument (nie Ty) musi albo zgadywać, albo przeszukiwać dokumentację, zwłaszcza pod kątem takich rzeczy jak
Tuple<int, int>
.Używałbym ich dla członków prywatnych / wewnętrznych, ale klas wyników dla członków publicznych / chronionych.
Ta odpowiedź zawiera również pewne informacje.
źródło
StringOps
(w zasadzie klasa "metod rozszerzających" dla JavyString
) ma metodęparition(Char => Boolean): (String, String)
(przyjmuje predykat, zwraca krotkę z 2 łańcuchami). Jest oczywiste, jakie są dane wyjściowe (oczywiście jeden będzie znak, dla którego predykat był prawdziwy, a drugi, dla którego był fałszywy, ale nie jest jasne, który jest który). To dokumentacja wyjaśnia ten problem (a dopasowywanie wzorców może służyć do wyjaśnienia w użyciu, które jest które).Istnieją rzeczywiście inne cenne zastosowania
Tuple<>
- większość z nich polega na abstrahowaniu od semantyki określonej grupy typów, które mają podobną strukturę, i traktowaniu ich po prostu jako uporządkowanego zbioru wartości. We wszystkich przypadkach zaletą krotek jest to, że pozwalają uniknąć zaśmiecania przestrzeni nazw klasami tylko do danych, które ujawniają właściwości, ale nie metody.Oto przykład rozsądnego użycia
Tuple<>
:W powyższym przykładzie chcemy reprezentować parę przeciwników, krotka jest wygodnym sposobem parowania tych instancji bez konieczności tworzenia nowej klasy. Oto kolejny przykład:
Układ w pokera można traktować jako po prostu zestaw kart - a krotka (może być) rozsądnym sposobem wyrażenia tego pojęcia.
Zwracanie silnie wpisanych
Tuple<>
wystąpień jako części publicznego interfejsu API dla typu publicznego rzadko jest dobrym pomysłem. Jak sam wiesz, krotki wymagają od zaangażowanych stron (autora biblioteki, użytkownika biblioteki) uzgodnienia z wyprzedzeniem celu i interpretacji używanych typów krotek. Tworzenie interfejsów API, które są intuicyjne i przejrzyste, a ichTuple<>
publiczne używanie jedynie przesłania intencje i zachowanie API, jest wystarczająco trudne .Typy anonimowe są również rodzajem krotki - są jednak silnie wpisane i pozwalają określić jasne, informacyjne nazwy dla właściwości należących do typu. Ale typy anonimowe są trudne w użyciu w różnych metodach - zostały one głównie dodane do obsługi technologii, takich jak LINQ, w których projekcje tworzyłyby typy, do których normalnie nie chcielibyśmy przypisywać nazw. (Tak, wiem, że typy anonimowe o tych samych typach i nazwanych właściwościach są konsolidowane przez kompilator).
Moja praktyczna zasada brzmi: jeśli zwrócisz go z publicznego interfejsu - nadaj mu nazwany typ .
Moja druga praktyczna zasada dotycząca używania krotek to: nazwa argumenty metody i zmienne lokalne typu
Tuple<>
tak jasno, jak to tylko możliwe - spraw, aby nazwa przedstawiała znaczenie relacji między elementami krotki. Pomyśl o moimvar opponents = ...
przykładzie.Oto przykład rzeczywistego przypadku, w którym
Tuple<>
unikałem deklarowania typu tylko do danych do użytku tylko w moim własnym zestawie . Sytuacja ta polega na tym, że przy używaniu słowników generycznych zawierających typy anonimowe trudno jest użyćTryGetValue()
metody do wyszukiwania elementów w słowniku, ponieważ metoda wymagaout
parametru, którego nie można nazwać:PS Istnieje inny (prostszy) sposób na obejście problemów wynikających z anonimowych typów w słownikach, a jest to użycie
var
słowa kluczowego, aby kompilator „wywnioskował” typ za Ciebie. Oto ta wersja:źródło
Tuple<>
może mieć sens. Zawsze są alternatywy ...Krotki mogą być przydatne ... ale później mogą też być uciążliwe. Jeśli masz metodę, która zwraca,
Tuple<int,string,string,int>
skąd wiesz, jakie te wartości są później. ByliID, FirstName, LastName, Age
czy byliUnitNumber, Street, City, ZipCode
.źródło
Krotki są dość rozczarowującym dodatkiem do środowiska CLR z punktu widzenia programisty C #. Jeśli masz kolekcję elementów o różnej długości, nie potrzebujesz, aby w czasie kompilacji miały unikalne nazwy statyczne.
Ale jeśli masz zbiór o stałej długości, oznacza to, że ustalone lokalizacje w kolekcji mają określone, wstępnie zdefiniowane znaczenie. I to jest zawsze lepiej, aby dać im odpowiednie nazwy statycznych w tym przypadku, zamiast pamiętać znaczenia
Item1
,Item2
itpKlasy anonimowe w C # już zapewniają doskonałe rozwiązanie dla najczęściej spotykanego prywatnego użycia krotek i nadają elementom znaczące nazwy, więc w rzeczywistości są pod tym względem lepsze. Jedynym problemem jest to, że nie mogą one wyciekać z nazwanych metod. Wolałbym, aby to ograniczenie zostało zniesione (być może tylko dla metod prywatnych), niż ma specyficzną obsługę krotek w C #:
źródło
struct var SimpleStruct {int x, int y;}
Zdefiniowanie struktury o nazwie rozpoczynającej się od guid związanej z prostymi strukturami i ma{System.Int16 x}{System.Int16 y}
dołączone coś takiego ; każda nazwa zaczynająca się od tego GUID byłaby wymagana do reprezentowania struktury zawierającej tylko te elementy i określoną określoną zawartość; jeśli w zakresie znajduje się wiele definicji dla tej samej nazwy, które są zgodne, wszystkie zostaną uznane za tego samego typu.ref
parametru, jedynym sposobem, aby mieć jednego delegata, który można przekazać do obu funkcji, będzie to, że jedna osoba utworzy i skompiluje typ delegata i przekazuje go innemu użytkownikowi do kompilacji. Nie ma możliwości, aby obie osoby mogły wskazać, że te dwa typy powinny być uważane za synonimy.Tuple
? Właśnie przejrzałem 50 miejsc w mojej bazie kodu, w których używam krotek i wszystkie są albo wartościami zwracanymi (zazwyczajyield return
), albo są elementami kolekcji, które są używane gdzie indziej, więc nie wybieraj klas anonimowych.Podobnie jak słowo kluczowe
var
, ma służyć wygodzie - ale jest równie łatwo nadużywane.Moim najbardziej skromnym zdaniem nie wystawiaj się
Tuple
jako klasa powrotna. Używaj go prywatnie, jeśli wymaga tego struktura danych usługi lub komponentu, ale zwróć dobrze sformułowane dobrze znane klasy z metod publicznych.źródło
var
nie można go przekazać ani istnieć poza zadeklarowanym zakresem. jednak nadal można go używać w sposób wykraczający poza jego przeznaczenie i być „nadużywanym”Używanie klasy takiej jak
ResultType
jest bardziej przejrzyste. Możesz nadać znaczące nazwy polom w klasie (podczas gdy w przypadku krotki będą one nazywaneItem1
iItem2
). Jest to jeszcze ważniejsze, jeśli typy obu pól są takie same: nazwa wyraźnie je rozróżnia.źródło
A co powiesz na użycie krotek we wzorcu dekoruj-sortuj-dekoruj? (Transformacja Schwartzian dla ludzi Perl). Oto wymyślony przykład, ale krotki wydają się być dobrym sposobem radzenia sobie z tego rodzaju problemami:
Teraz mogłem użyć Object [] dwóch elementów lub w tym konkretnym przykładzie łańcucha [] dwóch elementów. Chodzi o to, że mogłem użyć czegokolwiek jako drugiego elementu krotki, która jest używana wewnętrznie i jest dość łatwa do odczytania.
źródło
IMO te „krotki” to w zasadzie wszystkie
struct
typy anonimowe dostępu publicznego z nienazwanymi członkami .Tylko umieścić użyłbym krotka jest, gdy trzeba szybko blob razem niektóre dane, w bardzo ograniczonym zakresie. Semantyka danych powinna być oczywista , więc kod nie jest trudny do odczytania. Więc użycie krotki (
int
,int
) dla (row, col) wydaje się rozsądne. Ale ciężko mi znaleźć przewagę nadstruct
z nazwanymi członkami (więc nie popełnia się błędów, a wiersz / kolumna nie są przypadkowo zamieniane)Jeśli wysyłasz dane z powrotem do dzwoniącego lub akceptujesz dane od dzwoniącego, naprawdę powinieneś używać
struct
z nazwanymi członkami.Weź prosty przykład:
Wersja krotki
Nie widzę żadnej korzyści z używania krotki zamiast struktury z nazwanymi członkami. Korzystanie z nienazwanych członków jest krokiem wstecz pod względem czytelności i zrozumiałości kodu.
Tuple wydaje mi się leniwy sposób na uniknięcie tworzenia
struct
nazwanych członków. Nadużywanie krotki, gdzie naprawdę czujesz, że ty / lub ktoś inny napotykający twój kod potrzebowałby nazwanych członków, jest złą rzeczą ™, jeśli kiedykolwiek widziałem.źródło
Nie oceniaj mnie, nie jestem ekspertem, ale dzięki nowym krotkom w C # 7.x możesz teraz zwrócić coś takiego:
Przynajmniej teraz możesz go nazwać, a programiści mogą zobaczyć pewne informacje.
źródło
To oczywiście zależy! Jak już powiedziałeś, krotka może zaoszczędzić kod i czas, gdy chcesz zgrupować niektóre elementy do lokalnego spożycia. Możesz także użyć ich do stworzenia bardziej ogólnych algorytmów przetwarzania niż w przypadku przekazania konkretnej klasy. Nie pamiętam, ile razy żałowałem, że nie mam czegoś poza KeyValuePair lub DataRow, aby szybko przekazać jakąś datę z jednej metody do drugiej.
Z drugiej strony można z tym przesadzić i omijać krotki, w których można tylko zgadywać, co zawierają. Jeśli zamierzasz używać krotki między klasami, być może lepiej byłoby utworzyć jedną konkretną klasę.
Oczywiście używane z umiarem krotki mogą prowadzić do bardziej zwięzłego i czytelnego kodu. Możesz zajrzeć do C ++, STL i Boost, aby zobaczyć przykłady użycia krotek w innych językach, ale ostatecznie wszyscy będziemy musieli poeksperymentować, aby dowiedzieć się, jak najlepiej pasują do środowiska .NET.
źródło
Krotki są bezużyteczną funkcją frameworka w .NET 4. Myślę, że w C # 4.0 przegapiono świetną okazję. Bardzo chciałbym mieć krotki z wymienionych członków, więc można uzyskać dostęp do różnych dziedzinach krotki wg nazwy zamiast Wartość1 , wartość2 , etc ...
Wymagałoby to zmiany języka (składni), ale byłoby bardzo przydatne.
źródło
let success, value = MethodThatReturnsATuple()
Osobiście nigdy nie użyłbym krotki jako typu zwracanego, ponieważ nie ma wskazania, co reprezentują wartości. Krotki mają kilka cennych zastosowań, ponieważ w przeciwieństwie do obiektów są typami wartości i dlatego rozumieją równość. Z tego powodu użyję ich jako kluczy słownikowych, jeśli potrzebuję klucza wieloczęściowego lub jako klucza do klauzuli GroupBy, jeśli chcę grupować według wielu zmiennych i nie chcę zagnieżdżonych grup (Kto kiedykolwiek chce zagnieżdżonych grup?). Aby rozwiązać problem z ekstremalną szczegółowością, możesz je utworzyć za pomocą metody pomocniczej. Pamiętaj, że jeśli często uzyskujesz dostęp do członków (poprzez Item1, Item2 itp.), Prawdopodobnie powinieneś użyć innej konstrukcji, takiej jak struktura lub klasa anonimowa.
źródło
Użyłem krotek, zarówno tych
Tuple
nowych , jak i nowychValueTuple
, w wielu różnych scenariuszach i doszedłem do następującego wniosku: nie używaj .Za każdym razem napotykałem następujące problemy:
Moim zdaniem krotki są szkodą, a nie funkcją języka C #.
Mam trochę podobną, ale dużo mniej surową krytykę
Func<>
iAction<>
. Są one przydatne w wielu sytuacjach, szczególnie w prostychAction
iFunc<type>
wariantach, ale poza tym stwierdziłem, że tworzenie typu delegata jest bardziej eleganckie, czytelne, łatwe w utrzymaniu i zapewnia więcej funkcji, takich jakref
/out
parameters.źródło
ValueTuple
- i też ich nie lubię. Po pierwsze, nazewnictwo nie jest mocne, to raczej sugestia, bardzo łatwa do zepsucia, ponieważ można ją niejawnie przypisać do jednegoTuple
lubValueTuple
z tą samą liczbą i typami przedmiotów. Po drugie, brak dziedziczenia i konieczność skopiowania wklej całej definicji z miejsca na miejsce to nadal szkoda.