To rodzaj ogólnego pytania (ale używam C #), jaki jest najlepszy sposób (najlepsza praktyka), czy zwracasz wartość zerową lub pustą kolekcję dla metody, która ma kolekcję jako typ zwracany?
c#
collections
Omu
źródło
źródło
Odpowiedzi:
Pusta kolekcja. Zawsze.
To jest do bani:
Uznaje się za najlepszą praktykę, aby NIGDY nie zwracać
null
przy zwracaniu kolekcji lub listy. ZAWSZE zwracaj pusty wyliczalny / zbiór. Zapobiega wspomnianym bzdurom i zapobiega popychaniu samochodu przez współpracowników i użytkowników twoich zajęć.Mówiąc o nieruchomościach, zawsze ustaw swoją właściwość raz i zapomnij o niej
W .NET 4.6.1 można to bardzo mocno skondensować:
Mówiąc o metodach, które zwracają wyliczenia, możesz łatwo zwrócić pusty wyliczenie zamiast
null
...Używanie
Enumerable.Empty<T>()
może być postrzegane jako bardziej wydajne niż zwracanie, na przykład nowej pustej kolekcji lub tablicy.źródło
IEnumerable
czyICollection
nie. W każdym razie, jeśli wybierzesz coś w rodzaju,ICollection
oni również zwrócąnull
... Chciałbym, aby zwrócili pustą kolekcję, ale natknąłem się na ich powrótnull
, więc pomyślałem, że po prostu wspomnę o tym tutaj. Powiedziałbym, że domyślna kolekcja wyliczalnych jest pusta, a nie zerowa. Nie wiedziałem, że to taki delikatny temat.Z wytycznych projektowania ramowego, wydanie drugie (str. 256):
Oto kolejny interesujący artykuł o korzyściach płynących z nie zwracania wartości zerowych (próbowałem znaleźć coś na blogu Brada Abrama, a on link do tego artykułu).
Edytuj - ponieważ Eric Lippert skomentował już oryginalne pytanie, chciałbym również zamieścić link do jego doskonałego artykułu .
źródło
Zależy od umowy i konkretnego przypadku . Ogólnie najlepiej jest zwracać puste kolekcje , ale czasami ( rzadko ):
null
może oznaczać coś bardziej konkretnego;null
.Niektóre konkretne przykłady:
null
oznaczałoby to brak elementu, a pusta kolekcja spowodowałaby nadmiar (i być może niepoprawny)<collection />
źródło
Jest jeszcze jeden punkt, o którym jeszcze nie wspomniano. Rozważ następujący kod:
Język C # zwróci pusty moduł wyliczający podczas wywoływania tej metody. Dlatego, aby zachować spójność z projektem języka (a tym samym oczekiwaniami programisty), należy zwrócić pustą kolekcję.
źródło
Pusty jest o wiele bardziej przyjazny dla konsumenta.
Istnieje jasna metoda tworzenia pustego wyliczenia:
źródło
Wydaje mi się, że powinieneś zwrócić wartość, która jest semantycznie poprawna w kontekście, cokolwiek by to nie było. Reguła, która mówi „zawsze zwracaj pustą kolekcję” wydaje mi się trochę uproszczona.
Załóżmy, że w systemie szpitalnym mamy funkcję, która ma zwrócić listę wszystkich poprzednich hospitalizacji z ostatnich 5 lat. Jeśli klient nie był w szpitalu, warto zwrócić pustą listę. Ale co, jeśli klient pozostawił tę część formularza wstępu pustą? Potrzebujemy innej wartości, aby odróżnić „pustą listę” od „brak odpowiedzi” lub „nie wiem”. Możemy rzucić wyjątek, ale niekoniecznie jest to warunek błędu i niekoniecznie wyprowadza nas z normalnego przepływu programu.
Często byłem sfrustrowany systemami, które nie potrafią rozróżnić od zera do braku odpowiedzi. Wielokrotnie system prosił mnie o podanie pewnej liczby, wpisuję zero i pojawia się komunikat o błędzie z informacją, że muszę wprowadzić wartość w tym polu. Właśnie tak zrobiłem: wpisałem zero! Ale nie przyjmie zera, ponieważ nie może odróżnić go od braku odpowiedzi.
Odpowiedz Saundersowi:
Tak, zakładam, że istnieje różnica między „Osoba nie odpowiedziała na pytanie” a „Odpowiedź była zerowa”. To był punkt ostatniego akapitu mojej odpowiedzi. Wiele programów nie jest w stanie odróżnić „nie wiem” od pustego lub zerowego, co wydaje mi się potencjalnie poważną wadą. Na przykład kupowałem dom jakiś rok temu. Poszedłem na stronę z nieruchomościami i było tam wiele domów z ceną wywoławczą 0 USD. Brzmiało dla mnie całkiem dobrze: oddają te domy za darmo! Ale jestem pewien, że smutną rzeczywistością było to, że po prostu nie podali ceny. W takim przypadku możesz powiedzieć: „Cóż, oczywiście zero oznacza, że nie podali ceny - nikt nie oddaje domu za darmo”. Ale na stronie wymieniono również średnie ceny sprzedaży i sprzedaży domów w różnych miastach. Nie mogę przestać się zastanawiać, czy średnia nie zawierała zer, dając w ten sposób nieprawidłowo niską średnią dla niektórych miejsc. tj. jaka jest średnia 100 000 USD; 120 000 USD; i „nie wiem”? Technicznie odpowiedź brzmi „nie wiem”. To, co prawdopodobnie naprawdę chcemy zobaczyć, to 110 000 USD. Ale prawdopodobnie dostaniemy 73 333 USD, co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? dając w ten sposób nieprawidłowo niską średnią dla niektórych miejsc. tj. jaka jest średnia 100 000 USD; 120 000 USD; i „nie wiem”? Technicznie odpowiedź brzmi „nie wiem”. To, co prawdopodobnie naprawdę chcemy zobaczyć, to 110 000 USD. Ale prawdopodobnie dostaniemy 73 333 USD, co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? dając w ten sposób nieprawidłowo niską średnią dla niektórych miejsc. tj. jaka jest średnia 100 000 USD; 120 000 USD; i „nie wiem”? Technicznie odpowiedź brzmi „nie wiem”. To, co prawdopodobnie naprawdę chcemy zobaczyć, to 110 000 USD. Ale prawdopodobnie dostaniemy 73 333 USD, co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”?
RE ma dwie oddzielne funkcje, „czy jest jakaś?” i „jeśli tak, co to jest?” Tak, na pewno możesz to zrobić, ale dlaczego miałbyś chcieć? Teraz program wywołujący musi wykonać dwa połączenia zamiast jednego. Co się stanie, jeśli programista nie wywoła „any”? i przechodzi od razu do „co to jest?” ? Czy program zwróci błędne zero? Rzuć wyjątek? Zwrócić niezdefiniowaną wartość? Tworzy więcej kodu, więcej pracy i więcej potencjalnych błędów.
Jedyną korzyścią, jaką widzę, jest to, że pozwala ci przestrzegać arbitralnej reguły. Czy jest jakaś zaleta tej reguły, która sprawia, że warto kłopotać się jej przestrzeganiem? Jeśli nie, to po co?
Odpowiedz na Jammycakes:
Zastanów się, jak wyglądałby rzeczywisty kod. Wiem, że pytanie brzmiało C #, ale przepraszam, jeśli piszę w Javie. Moje C # nie jest bardzo ostre i zasada jest taka sama.
Ze znakiem zerowym:
Z osobną funkcją:
W rzeczywistości jest to wiersz lub dwa mniej kodu ze znakiem zerowym, więc nie stanowi większego obciążenia dla osoby dzwoniącej, jest mniej.
Nie rozumiem, jak to powoduje problem z SUCHEM. To nie tak, że musimy wykonać połączenie dwukrotnie. Gdybyśmy zawsze chcieli zrobić to samo, gdy lista nie istnieje, być może moglibyśmy przesunąć obsługę do funkcji get-list, zamiast wywoływania wywoływania, a więc umieszczenie kodu w wywołującym byłoby naruszeniem DRY. Ale prawie na pewno nie chcemy zawsze robić tego samego. W funkcjach, w których musimy mieć listę do przetworzenia, brakująca lista jest błędem, który może zatrzymać przetwarzanie. Ale na ekranie edycji z pewnością nie chcemy przerwać przetwarzania, jeśli jeszcze nie wprowadzili danych: chcemy pozwolić im na wprowadzanie danych. Tak więc obsługa „brak listy” musi odbywać się na poziomie osoby dzwoniącej w taki czy inny sposób. A czy robimy to z zerowym znakiem powrotu czy osobną funkcją, nie ma znaczenia dla większej zasady.
Jasne, jeśli program wywołujący nie sprawdzi wartości null, program może zawieść z wyjątkiem wyjątku wskaźnika zerowego. Ale jeśli istnieje osobna funkcja „ma jakąkolwiek”, a osoba dzwoniąca nie wywołuje tej funkcji, ale ślepo wywołuje funkcję „pobierz listę”, to co się dzieje? Jeśli zgłasza wyjątek lub w inny sposób zawiedzie, cóż, to prawie tak samo, jak by się stało, gdyby zwrócił wartość null i nie sprawdził go. Jeśli zwraca pustą listę, to po prostu źle. Nie rozróżniasz między „Mam listę z zerowymi elementami” a „Nie mam listy”. To jak zwracanie zera ceny, gdy użytkownik nie podał żadnej ceny: to po prostu źle.
Nie rozumiem, w jaki sposób dołączenie dodatkowego atrybutu do kolekcji pomaga. Dzwoniący nadal musi to sprawdzić. Dlaczego to jest lepsze niż sprawdzanie wartości null? Ponownie absolutną najgorszą rzeczą, jaka może się zdarzyć, jest zapomnienie przez programistę o sprawdzeniu i podanie niepoprawnych wyników.
Funkcja, która zwraca null, nie jest zaskoczeniem, jeśli programista zna pojęcie wartości null oznaczające „nie ma wartości”, o czym myślę, że każdy kompetentny programista powinien słyszeć, niezależnie od tego, czy uważa, że to dobry pomysł, czy nie. Myślę, że posiadanie oddzielnej funkcji jest raczej problemem „zaskoczenia”. Jeśli programista nie zna interfejsu API, gdy uruchomi test bez danych, szybko odkryje, że czasami odzyskuje wartość zerową. Ale jak odkryłby istnienie innej funkcji, gdyby nie przyszło mu do głowy, że taka funkcja może istnieć, i sprawdza dokumentację, a dokumentacja jest kompletna i zrozumiała? Wolałbym mieć jedną funkcję, która zawsze daje mi znaczącą odpowiedź, niż dwie funkcje, które muszę znać i pamiętać, aby wywołać oba.
źródło
Jeśli pusta kolekcja ma sens semantyczny, wolę to zwrócić. Zwracanie pustej kolekcji do
GetMessagesInMyInbox()
komunikowania „naprawdę nie masz żadnych wiadomości w skrzynce odbiorczej”, podczas gdy zwracanienull
może być przydatne, aby poinformować, że dostępne są niewystarczające dane, aby powiedzieć, jak powinna wyglądać zwrócona lista.źródło
null
wartość na pewno nie wydaje się rozsądna, myślałem o niej bardziej ogólnie. Wyjątki są również świetne do informowania o tym, że coś poszło nie tak, ale jeśli „niewystarczające dane”, o których mowa, są doskonale oczekiwane, to rzucenie wyjątku byłoby złym projektem. Myślę raczej o scenariuszu, w którym jest to całkowicie możliwe i nie ma w ogóle błędu, że metoda czasami nie jest w stanie obliczyć odpowiedzi.Zwracanie wartości null może być bardziej wydajne, ponieważ nie jest tworzony nowy obiekt. Często wymagałoby to jednak
null
sprawdzenia (lub obsługi wyjątków).Semantycznie
null
i pusta lista nie oznaczają tego samego. Różnice są subtelne i w niektórych przypadkach jeden wybór może być lepszy niż drugi.Niezależnie od wyboru udokumentuj go, aby uniknąć zamieszania.
źródło
Można argumentować, że uzasadnienie Null Object Pattern jest podobne do argumentu przemawiającego za zwróceniem pustej kolekcji.
źródło
Zależy od sytuacji. Jeśli jest to szczególny przypadek, zwróć null. Jeśli funkcja zwraca akurat pustą kolekcję, to oczywiście zwracanie jest w porządku. Jednak zwracanie pustej kolekcji jako specjalnego przypadku z powodu niepoprawnych parametrów lub z innych powodów NIE jest dobrym pomysłem, ponieważ maskuje specjalny przypadek.
Właściwie w tym przypadku zazwyczaj wolę rzucić wyjątek, aby upewnić się, że NAPRAWDĘ nie jest ignorowany :)
Mówienie, że sprawia, że kod jest bardziej niezawodny (przez zwrócenie pustej kolekcji), ponieważ nie muszą one obsługiwać warunku zerowego, jest złe, ponieważ po prostu maskuje problem, który powinien zostać rozwiązany przez kod wywołujący.
źródło
Twierdziłbym, że
null
to nie to samo, co pusta kolekcja i powinieneś wybrać, która najlepiej reprezentuje to, co oddajesz. W większości przypadkównull
jest to nic (oprócz SQL). Pusta kolekcja to coś, choć coś pustego.Jeśli musisz wybrać jedno lub drugie, powiedziałbym, że powinieneś raczej wybierać pustą kolekcję niż zerową. Ale są chwile, kiedy pusta kolekcja nie jest tym samym, co wartość pusta.
źródło
Zawsze myśl na korzyść swoich klientów (którzy używają interfejsu API):
Zwracanie „null” bardzo często powoduje problemy z klientami niepoprawnie obsługującymi kontrole null, co powoduje wyjątek NullPointerException podczas działania. Widziałem przypadki, w których takie brakujące sprawdzanie wartości zerowej wymuszało priorytetowy problem z produkcją (klient używał foreach (...) dla wartości zerowej). Podczas testowania problem nie wystąpił, ponieważ operowane dane były nieco inne.
źródło
Chciałbym tu wyjaśnić, podając odpowiedni przykład.
Rozważ przypadek tutaj ..
Tutaj rozważ funkcje, których używam ..
Mogę łatwo używać
ListCustomerAccount
iFindAll
zamiast.,UWAGA: Ponieważ AccountValue nie jest
null
, funkcja Sum () nie zwrócinull
., Dlatego mogę z niej korzystać bezpośrednio.źródło
Dyskutowaliśmy w zespole programistów w pracy około tydzień temu i prawie jednogłośnie wybraliśmy pustą kolekcję. Jedna osoba chciała zwrócić wartość zerową z tego samego powodu, który Mike wskazał powyżej.
źródło
Pusta kolekcja. Jeśli używasz C #, założeniem jest, że maksymalizacja zasobów systemowych nie jest niezbędna. Chociaż mniej wydajne, zwracanie Pustej Kolekcji jest znacznie wygodniejsze dla zaangażowanych programistów (z tego powodu zostanie opisany powyżej).
źródło
Zwracanie pustej kolekcji jest lepsze w większości przypadków.
Powodem tego jest wygoda implementacji dzwoniącego, spójna umowa i łatwiejsza implementacja.
Jeśli metoda zwraca wartość null w celu wskazania pustego wyniku, program wywołujący musi oprócz implementacji wyliczenia zaimplementować adapter sprawdzania wartości null. Ten kod jest następnie duplikowany w różnych obiektach wywołujących, więc dlaczego nie umieścić tego adaptera w metodzie, aby można go było ponownie użyć.
Prawidłowe użycie wartości null dla IEnumerable może wskazywać na brak wyniku lub błąd operacji, ale w takim przypadku należy rozważyć inne techniki, takie jak zgłoszenie wyjątku.
źródło
Zobacz tutaj
null
ogólnie misterną burzę gówna . Nie zgadzam się ze stwierdzeniem, któreundefined
jest innenull
, ale nadal warto je przeczytać. I wyjaśnia, dlaczegonull
w ogóle powinieneś unikać, a nie tylko w przypadku, o który prosiłeś. Istotą jest to, żenull
w każdym języku jest to szczególny przypadek. Musisz myśleć o tymnull
jako o wyjątku.undefined
różni się tym, że kod zajmujący się niezdefiniowanym zachowaniem jest w większości przypadków tylko błędem. C i większość innych języków ma również niezdefiniowane zachowanie, ale większość z nich nie ma identyfikatora tego w języku.źródło
Z punktu widzenia zarządzania złożonością, głównym celem inżynierii oprogramowania, chcemy uniknąć propagowania niepotrzebnej cykliczności złożoności wśród klientów interfejsu API. Zwracanie wartości zerowej do klienta jest jak zwracanie cyklicznego kosztu złożoności innej gałęzi kodu.
(Odpowiada to obciążeniu związanemu z testowaniem jednostkowym. Konieczne byłoby napisanie testu dla zerowej skrzynki zwrotnej, oprócz pustej skrzynki zwrotnej kolekcji).
źródło