Czy używanie krotek .NET 4.0 w moim kodzie C # jest złą decyzją projektową?

166

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 ResultTypejest 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 Itemreprezentuje każdy z nich , ale piszesz i utrzymujesz mniej kodu.

Będziemy bardzo wdzięczni za wszelkie doświadczenia związane z tym wyborem.

Jason Webb
źródło
16
@Boltbait: ponieważ krotka pochodzi z teorii mnogości en.wikipedia.org/wiki/Tuple
Matthew Whited
16
@BoltBait: „Krotka” to uporządkowany zbiór wartości, który niekoniecznie ma nic wspólnego z wierszami w bazie danych.
FrustratedWithFormsDesigner
19
Krotki w c # byłyby jeszcze ładniejsze, gdyby była jakaś dobra akcja rozpakowywania krotki :)
Justin
4
@John Saunders Moje pytanie dotyczy konkretnie decyzji projektowych w języku c #. Nie używam żadnych innych języków .net, więc nie jestem pewien, jak wpływają na nie krotki. Więc oznaczyłem to c #. Wydawało mi się to rozsądne, ale myślę, że zmiana na .net też jest w porządku.
Jason Webb
13
@John Saunders: Jestem pewien, że pyta o decyzje projektowe dotyczące używania lub nie używania krotek w aplikacji napisanej wyłącznie w języku C #. Nie sądzę, żeby pytał o decyzje projektowe, które wpłynęły na tworzenie języka C #.
Brett Widmeier

Odpowiedzi:

163

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.

Bryan Watts
źródło
19
Różnica między publicznym a prywatnym jest naprawdę ważna, dziękuję za poruszenie tej kwestii. Zwracanie krotek w publicznym API jest naprawdę złym pomysłem, ale wewnętrznie tam, gdzie rzeczy mogą być ściśle powiązane (ale to w porządku ), jest w porządku.
Clinton Pierce
36
Ciasno lub mocno skręcony? ;)
contactmatt
Czy odpowiednia dokumentacja nie mogłaby się tutaj przydać? Na przykład Scala StringOps(w zasadzie klasa "metod rozszerzających" dla Javy String) 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).
Kat
@Mike: To sprawia, że ​​trudno jest uzyskać właściwą i łatwo popełnić błąd, cecha charakterystyczna interfejsu API, który sprawi komuś ból gdzieś :-)
Bryan Watts
79

Z mojego punktu widzenia krotka jest skrótem do napisania klasy wynikowej (jestem pewien, że są też inne zastosowania).

Istnieją rzeczywiście inne cenne zastosowaniaTuple<> - 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<>:

var opponents = new Tuple<Player,Player>( playerBob, playerSam );

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:

var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );

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.

odkładając na bok możliwość, że brakuje mi sensu krotek, czy przykład z krotką jest złym wyborem projektowym?

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 ich Tuple<>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 moim var 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 wymaga outparametru, którego nie można nazwać:

public static class DictionaryExt 
{
    // helper method that allows compiler to provide type inference
    // when attempting to locate optionally existent items in a dictionary
    public static Tuple<TValue,bool> Find<TKey,TValue>( 
        this IDictionary<TKey,TValue> dict, TKey keyToFind ) 
    {
        TValue foundValue = default(TValue);
        bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
        return Tuple.Create( foundValue, wasFound );
    }
}

public class Program
{
    public static void Main()
    {
        var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
                             new { LastName = "Sanders", FirstName = "Bob" } };

        var peopleDict = people.ToDictionary( d => d.LastName );

        // ??? foundItem <= what type would you put here?
        // peopleDict.TryGetValue( "Smith", out ??? );

        // so instead, we use our Find() extension:
        var result = peopleDict.Find( "Smith" );
        if( result.First )
        {
            Console.WriteLine( result.Second );
        }
    }
}

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 varsłowa kluczowego, aby kompilator „wywnioskował” typ za Ciebie. Oto ta wersja:

var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
   // use foundItem...
}
LBushkin
źródło
5
@stakx: Tak, zgadzam się. Pamiętaj jednak - przykład ma w dużej mierze zilustrować przypadki, w których użycie a Tuple<> może mieć sens. Zawsze są alternatywy ...
LBushkin
1
Myślę, że zastąpienie parametrów krotką, tak jak w TryGetValue lub TryParse, jest jednym z niewielu przypadków, w których może mieć sens, aby publiczne API zwracało krotkę. W rzeczywistości co najmniej jeden język .NET automatycznie wprowadza tę zmianę w interfejsie API.
Joel Mueller
1
@stakx: Krotki są również niezmienne, podczas gdy tablice nie.
Robert Paulson
2
Zrozumiałem, że krotki są przydatne tylko jako niezmienne obiekty gracza w pokera.
Gennady Vanin Геннадий Ванин
2
+1 Świetna odpowiedź - uwielbiam twoje dwa początkowe przykłady, które właściwie zmieniły moje zdanie. Nigdy wcześniej nie widziałem przykładów, w których krotka byłaby co najmniej tak odpowiednia, jak niestandardowa struktura lub typ. Jednak twój ostatni „przykład z prawdziwego życia” wydaje mi się nie dodawać już zbyt wiele wartości. Pierwsze dwa przykłady są doskonałe i proste. To ostatnie jest trochę trudne do zrozumienia, a twój rodzaj "PS" czyni go bezużytecznym, ponieważ twoje alternatywne podejście jest znacznie prostsze i nie wymaga krotek.
chiccodoro
18

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. Byli ID, FirstName, LastName, Ageczy byli UnitNumber, Street, City, ZipCode.

Matthew Whited
źródło
9

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, Item2itp

Klasy 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 #:

private var GetDesserts()
{
    return _icecreams.Select(
        i => new { icecream = i, topping = new Topping(i) }
    );
}

public void Eat()
{
    foreach (var dessert in GetDesserts())
    {
        dessert.icecream.AddTopping(dessert.topping);
        dessert.Eat();
    }
}
Daniel Earwicker
źródło
Jako ogólną cechę chciałbym zobaczyć, że .net zdefiniowałby standard dla kilku specjalnych rodzajów typów anonimowych, takich jak np. 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.
supercat
Taka rzecz byłaby pomocna w przypadku kilku rodzajów typów, w tym zmiennych i niezmiennych klas i struktur, a także interfejsów i delegatów do użytku w sytuacjach, w których dopasowanie struktury powinno implikować dopasowaną semantykę. Chociaż z pewnością istnieją powody, dla których warto mieć dwa typy delegatów, które mają te same parametry, które nie są uznawane za zamienne, byłoby również pomocne, gdyby można było określić, że metoda wymaga delegata, który przyjmuje pewną kombinację parametrów, ale poza tym nie nie obchodzi mnie, jaki to delegat.
supercat
To niefortunne, że jeśli dwie różne osoby chcą napisać funkcje, które przyjmują jako delegatów wejściowych, które wymagają jednego refparametru, 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.
supercat
Czy masz jakieś bardziej konkretne przykłady, w których używałbyś klas anonimowych zamiast 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 (zazwyczaj yield return), albo są elementami kolekcji, które są używane gdzie indziej, więc nie wybieraj klas anonimowych.
2
@JonofAllTrades - opinie prawdopodobnie są różne, ale staram się projektować metody publiczne tak, jakby miały być używane przez kogoś innego, nawet jeśli to ktoś ja, więc zapewniam sobie dobrą obsługę klienta! Zwykle wywołujesz funkcję więcej razy niż ją piszesz. :)
Daniel Earwicker
8

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ę Tuplejako 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.

// one possible use of tuple within a private context. would never
// return an opaque non-descript instance as a result, but useful
// when scope is known [ie private] and implementation intimacy is
// expected
public class WorkflowHost
{
    // a map of uri's to a workflow service definition 
    // and workflow service instance. By convention, first
    // element of tuple is definition, second element is
    // instance
    private Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> _map = 
        new Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> ();
}
johnny g
źródło
2
Główne języki „RAD” (najlepszym przykładem jest Java) zawsze wybierały bezpieczeństwo i unikały strzelania do siebie w scenariuszach związanych z typem stopy. Cieszę się, że Microsoft ryzykuje z C # i daje językowi niezłą moc, nawet jeśli może być nadużywany.
Matt Greer,
4
Słowa kluczowego var nie można nadużywać. Może miałeś na myśli dynamikę?
Chris Marisic
@Chris Marisic, żeby wyjaśnić, nie miałem na myśli tego samego scenariusza - jesteś absolutnie poprawny, varnie 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”
johnny g
5
Nadal stoję na tym var nie może być nadużywany okres. Jest to tylko słowo kluczowe, które służy do tworzenia krótszej definicji zmiennej. Twój argument być może dotyczy typów anonimowych, ale nawet tych naprawdę nie można nadużywać, ponieważ nadal są normalnymi statycznie połączonymi typami, teraz dynamicznymi, to zupełnie inna historia.
Chris Marisic
4
var może być nadużywany w tym sensie, że jeśli jest nadużywany, może czasami powodować problemy dla osoby czytającej kod. Zwłaszcza jeśli nie pracujesz w programie Visual Studio i brakuje Ci inteligencji.
Matt Greer,
5

Używanie klasy takiej jak ResultTypejest bardziej przejrzyste. Możesz nadać znaczące nazwy polom w klasie (podczas gdy w przypadku krotki będą one nazywane Item1i Item2). Jest to jeszcze ważniejsze, jeśli typy obu pól są takie same: nazwa wyraźnie je rozróżnia.

Richard Fearn
źródło
Mała dodatkowa zaleta: możesz bezpiecznie zmienić kolejność parametrów dla klasy. Zrobienie tego w przypadku krotki spowoduje uszkodzenie kodu, a jeśli pola mają podobne typy, może to nie zostać wykryte w czasie kompilacji.
Zgadzam się, krotki są używane, gdy programista nie ma energii, aby wymyślić sensowną nazwę klasy, grupującą znaczące nazwy właściwości. W programowaniu chodzi o komunikację. Krotki są przeciwieństwem komunikacji. Wolałbym, aby Microsoft pozostawił to z języka, aby zmusić programistów do podejmowania dobrych decyzji projektowych.
mike gold
5

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:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] files = Directory.GetFiles("C:\\Windows")
                    .Select(x => new Tuple<string, string>(x, FirstLine(x)))
                    .OrderBy(x => x.Item2)
                    .Select(x => x.Item1).ToArray();
        }
        static string FirstLine(string path)
        {
            using (TextReader tr = new StreamReader(
                        File.Open(path, FileMode.Open)))
            {
                return tr.ReadLine();
            }
        }
    }
}

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.

Clinton Pierce
źródło
3
Możesz użyć Tuple.Create zamiast konstruktora. Jest krótszy.
Kugel
typ anonimowy będzie znacznie bardziej czytelny; jak przy użyciu rzeczywistych nazw zmiennych zamiast „x”
Dave Cousineau
Byłoby teraz . To jest problem SO, odpowiedzi opublikowane lata i lata temu nigdy nie są czyszczone ani aktualizowane pod kątem nowych technologii.
Clinton Pierce
4

IMO te „krotki” to w zasadzie wszystkie structtypy 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ę nad structz 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ć structz nazwanymi członkami.

Weź prosty przykład:

struct Color{ float r,g,b,a ; }
public void setColor( Color color )
{
}

Wersja krotki

public void setColor( Tuple<float,float,float,float> color )
{
  // why?
}

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 structnazwanych 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.

bobobobo
źródło
3

Nie oceniaj mnie, nie jestem ekspertem, ale dzięki nowym krotkom w C # 7.x możesz teraz zwrócić coś takiego:

return (string Name1, int Name2)

Przynajmniej teraz możesz go nazwać, a programiści mogą zobaczyć pewne informacje.

Denis Gordin
źródło
2

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.

Panagiotis Kanavos
źródło
1

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.

Philippe Leybaert
źródło
W jaki sposób krotki są „funkcją językową”? Jest to klasa dostępna dla wszystkich języków .net, prawda?
Jason Webb
11
Być może stosunkowo bezużyteczne dla C #. Ale System.Tuple nie został dodany do struktury z powodu C #. Został dodany, aby ułatwić współdziałanie między językiem F # a innymi językami.
Joel Mueller
@Jason: Nie powiedziałem, że to funkcja języka (pomyliłem się, ale poprawiłem to, zanim opublikowałeś ten komentarz).
Philippe Leybaert
2
@Jason - języki z bezpośrednią obsługą krotek mają niejawną obsługę dekonstruowania krotek na wartości składowe. Kod napisany w tych językach zazwyczaj nigdy, przenigdy nie uzyskuje dostępu do właściwości Item1, Item2. let success, value = MethodThatReturnsATuple()
Joel Mueller
@Joel: to pytanie jest oznaczone jako C #, więc w jakiś sposób dotyczy C #. Nadal myślę, że mogliby zamienić go w pierwszorzędny element języka.
Philippe Leybaert
1

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.

Seth Paulson
źródło
0

Użyłem krotek, zarówno tych Tuplenowych , jak i nowych ValueTuple, 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:

  • kod stał się nieczytelny z powodu braku silnego nazewnictwa;
  • nie można używać funkcji hierarchii klas, takich jak DTO klas bazowych i DTO klas potomnych itp .;
  • jeśli są używane w więcej niż jednym miejscu, kopiujesz i wklejasz te brzydkie definicje zamiast czystej nazwy klasy.

Moim zdaniem krotki są szkodą, a nie funkcją języka C #.

Mam trochę podobną, ale dużo mniej surową krytykę Func<>i Action<>. Są one przydatne w wielu sytuacjach, szczególnie w prostych Actioni Func<type>wariantach, ale poza tym stwierdziłem, że tworzenie typu delegata jest bardziej eleganckie, czytelne, łatwe w utrzymaniu i zapewnia więcej funkcji, takich jak ref/ outparameters.

Panie TA
źródło
Wspomniałem o nazwanych krotkach - 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 jednego Tuplelub ValueTuplez 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.
Pan TA
Zgadzam się z tymi punktami. Próbuję używać krotek tylko wtedy, gdy kontroluję producenta i konsumenta i jeśli nie są zbyt „daleko”. Znaczenie, producent i konsument w tej samej metodzie = „blisko”, podczas gdy producent i konsument w różnych zespołach = „lata świetlne stąd”. Więc jeśli utworzę krotki na początku metody i użyję ich na końcu? Jasne, nie przeszkadza mi to. Dokumentuję również warunki i tym podobne. Ale tak, zgadzam się, że generalnie to nie jest właściwy wybór.
Mike Christiansen