Rozważmy funkcję, która zwraca dwie wartości. Możemy pisać:
// Using out:
string MyFunction(string input, out int count)
// Using Tuple class:
Tuple<string, int> MyFunction(string input)
// Using struct:
MyStruct MyFunction(string input)
Która z nich jest najlepszą praktyką i dlaczego?
struct
jakEric
wspomniano.Odpowiedzi:
Każdy z nich ma swoje wady i zalety.
Nasze parametry są szybkie i tanie, ale wymagają przekazania zmiennej i polegają na mutacji. Prawidłowe użycie parametru out z LINQ jest prawie niemożliwe.
Krotki powodują presję na wyrzucanie elementów bezużytecznych i nie dokumentują się samemu. „Pozycja 1” nie jest zbyt opisowa.
Struktury niestandardowe mogą być kopiowane powoli, jeśli są duże, ale samodokumentują się i są wydajne, jeśli są małe. Jednak zdefiniowanie całej grupy niestandardowych struktur do trywialnych zastosowań jest również uciążliwe.
Byłbym skłonny do niestandardowego rozwiązania strukturalnego, aby wszystkie inne rzeczy były takie same. Jeszcze lepiej jest jednak utworzyć funkcję, która zwraca tylko jedną wartość . Dlaczego w pierwszej kolejności zwracasz dwie wartości?
AKTUALIZACJA: Zwróć uwagę, że krotki w języku C # 7, które zostały wysłane sześć lat po napisaniu tego artykułu, są typami wartości i dlatego są mniej prawdopodobne, że będą powodować presję kolekcji.
źródło
Dodając do poprzednich odpowiedzi, C # 7 wprowadza krotki typów wartości, w przeciwieństwie do
System.Tuple
tego typu referencyjnego, a także oferuje ulepszoną semantykę.Nadal możesz pozostawić je bez nazw i użyć
.Item*
składni:(string, string, int) getPerson() { return ("John", "Doe", 42); } var person = getPerson(); person.Item1; //John person.Item2; //Doe person.Item3; //42
Ale to, co jest naprawdę potężne w tej nowej funkcji, to możliwość nazwania krotek. Moglibyśmy więc przepisać powyższe w ten sposób:
(string FirstName, string LastName, int Age) getPerson() { return ("John", "Doe", 42); } var person = getPerson(); person.FirstName; //John person.LastName; //Doe person.Age; //42
Obsługiwana jest również destrukcja:
(string firstName, string lastName, int age) = getPerson()
źródło
Myślę, że odpowiedź zależy od semantyki tego, co robi funkcja i relacji między tymi dwiema wartościami.
Na przykład
TryParse
metody przyjmująout
parametr, który akceptuje przeanalizowaną wartość, i zwracają znak,bool
aby wskazać, czy analiza powiodła się. Te dwie wartości tak naprawdę nie pasują do siebie, więc semantycznie ma więcej sensu, a intencja kodu jest łatwiejsza do odczytania, użycieout
parametru.Jeśli jednak twoja funkcja zwraca współrzędne X / Y jakiegoś obiektu na ekranie, to te dwie wartości semantycznie należą do siebie i lepiej byłoby użyć
struct
.Osobiście unikałbym używania a
tuple
for cokolwiek, co będzie widoczne dla kodu zewnętrznego ze względu na niezręczną składnię pobierania członków.źródło
out
parametrnull
. Istnieje kilka niezmiennych typów, których wartość dopuszcza wartość zerową.try
wzorcem, który działa z kowariantnymi interfejsami jestT TryGetValue(whatever, out bool success)
; że podejście pozwoliłoby interfejsyIReadableMap<in TKey, out TValue> : IReadableMap<out TValue>
i pozwól kod, który chce map instancjiAnimal
do instancjiCar
do zaakceptowaniaDictionary<Cat, ToyotaCar>
[użyciemTryGetValue<TKey>(TKey key, out bool success)
. Taka wariancja nie jest możliwa, jeśliTValue
zostanie użyta jakoref
parametr.Pójdę za podejściem z użyciem parametru Out, ponieważ w drugim podejściu wymagałoby to utworzenia i obiektu klasy Tuple, a następnie dodania do niej wartości, co moim zdaniem jest kosztowną operacją w porównaniu do zwracania wartości z parametru out. Chociaż jeśli chcesz zwrócić wiele wartości w klasie Tuple (których w rzeczywistości nie można osiągnąć przez zwrócenie tylko jednego parametru wyjściowego), przejdę do drugiego podejścia.
źródło
out
. Dodatkowo istniejeparams
słowo kluczowe, o którym nie wspomniałem, aby odpowiedzieć na pytanie.Nie wspomniałeś o jeszcze jednej opcji, którą jest posiadanie niestandardowej klasy zamiast struktury. Jeśli dane mają skojarzoną semantykę, na której mogą być obsługiwane funkcje, lub rozmiar instancji jest wystarczająco duży (z reguły> 16 bajtów), preferowana może być klasa niestandardowa. Używanie „out” nie jest zalecane w publicznym interfejsie API ze względu na jego powiązanie ze wskaźnikami i wymagające zrozumienia, jak działają typy referencyjne.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Krotka jest dobra do użytku wewnętrznego, ale jest niewygodna w publicznym interfejsie API. Tak więc mój głos jest między strukturą a klasą dla publicznego interfejsu API.
źródło
Nie ma „najlepszych praktyk”. To jest to, z czym czujesz się komfortowo i co działa najlepiej w Twojej sytuacji. Jeśli jesteś z tym konsekwentny, nie ma problemu z żadnym z opublikowanych przez Ciebie rozwiązań.
źródło