Oczekiwane zachowanie, gdy żądanie kolekcji będzie zawierało zero elementów

13

Powiedzmy, że otrzymujesz następujące ...

List<Thing> theThings = fubar.Things.All();

Gdyby nie było nic do zwrócenia, czego można oczekiwać od fubar.Things.All ()?

Edycja: Dzięki za opinie. Zaczekam chwilę i zaakceptuję zgłoszenie z największą liczbą wzlotów.

Zgadzam się z dotychczasowymi odpowiedziami, szczególnie tymi sugerującymi pustą kolekcję. Sprzedawca dostarczył interfejs API z kilkoma wywołaniami podobnymi do powyższego przykładu. Sprzedawca, który w ubiegłym roku osiągnął przychody w wysokości 4,6 miliona USD dzięki swoim interfejsom API, BTW. Robią coś, z czym zasadniczo się nie zgadzam - rzucają wyjątek.

abscode
źródło
Wygląda na dość solidny konsensus [tutaj] [1]: Pusta kolekcja. Zawsze. [1]: stackoverflow.com/questions/1969993/...
Jesse C. Slicer
Do czego służy typ danych Things? Jeśli sensowne jest, aby Thingspole zwróciło wartość null, wówczas uzasadnione jest otrzymanie wyjątku, ponieważ przed wywołaniem nie sprawdzono wartości null All(). Zgadzam się jednak z ludźmi, którzy uważają, że fubar.Thingspowinien zwrócić pustą kolekcję zamiast wartości zerowej.
Colin D
Widzę o co ci chodzi, Colin. W takim przypadku możesz założyć, że Rzeczy istnieją, a All () jest statyczny. Wyjątek jest specyficzny dla pustej kolekcji, a nie z innego powodu.
abscode
OMG rzucają wyjątek ...! o_O
Stuart Marks
Teraz bardziej interesującym pytaniem byłoby, jaki powód, na ziemi, ktoś mógłby rzucić w tak ogólnej sprawie, lub co czyni tę skrzynkę tak wyjątkową, aby uzasadniała Rzucanie ??!
Martin Ba,

Odpowiedzi:

29

Z dwóch możliwości (tj. Zwrócenie a nulllub zwrócenie pustej kolekcji) wybrałbym zwrócenie pustej kolekcji, ponieważ pozwala to dzwoniącemu pominąć sprawdzenie zwróconej wartości. Zamiast pisać to

List<Thing> theThings = fubar.Things.All();
if (theThings != null) {
    for (Thing t : theThings) {
        t.doSomething();
    }
}

mogliby to napisać:

List<Thing> theThings = fubar.Things.All();
for (Thing t : theThings) {
    t.doSomething();
}

Ten drugi fragment kodu jest krótszy i łatwiejszy do odczytania, ponieważ poziom zagnieżdżenia jest niższy o jeden.

dasblinkenlight
źródło
2
Myślę, że łatwiej byłoby mi również zrozumieć koncepcyjnie, ponieważ „zestaw jest pusty” (brak przedmiotów). Null to „nie ma zestawu”, co jest zupełnie inne. (Powinno to również obejmować rzeczy, które są logicznie niemożliwe - zestaw wszystkich elementów, które są nieparzyste, a nawet parzyste, powinien być pusty, a nie zerowy). Naprawdę nie jestem pewien, co (logicznie) stanowiłoby zestaw zerowy ... (nawet jeśli jesteś naga na wyspie, twoje rzeczy są pustym zestawem, a nie zerowym)
Clockwork-Muse
@ X-Zero Ale jeśli jesteś nagi, „rzeczy w plecaku” mogą zwrócić zerowy zestaw, ponieważ nawet nie masz przy sobie plecaka. Może to być wyjątek BackpackNotFoundException, ale tylko wtedy, gdy jest to naprawdę nieoczekiwane. Powinien to być normalny stan, powiedzmy, w grze o przetrwanie na wyspie.
Izkata,
Dodatkowa kontrola zerowa pomaga mi spać w nocy.
Joel B,
6

Spodziewałbym się pustej listy. theThingsnadal będzie przedmiotem, ale theThings.Countczy theThings.size()wróci 0.

David Hogue
źródło
5

Takie problemy projektowe rozwiązuje wzorzec Null Object

... Zamiast używać odwołania zerowego do przekazywania braku obiektu (na przykład nieistniejącego klienta), używa się obiektu, który implementuje oczekiwany interfejs, ale którego treść metody jest pusta. Zaletą tego podejścia w porównaniu z działającą domyślną implementacją jest to, że obiekt zerowy jest bardzo przewidywalny i nie ma żadnych skutków ubocznych: nie robi nic.

Na przykład funkcja może pobrać listę plików w katalogu i wykonać na nich jakąś akcję. W przypadku pustego katalogu jedną odpowiedzią może być zgłoszenie wyjątku lub zwrócenie pustego odwołania zamiast listy. Zatem kod, który oczekuje na listę, musi sprawdzić, czy rzeczywiście ma ją przed kontynuowaniem, co może skomplikować projekt ...

Sugestia szczególnie przydatna w twoim przypadku (zwrot, Listgdy nie ma żadnych Thing) to:

... Zwracając zamiast tego pusty obiekt (tj. Pustą listę ), nie trzeba sprawdzać, czy zwracana wartość jest w rzeczywistości listą. Funkcja wywołująca może po prostu iterować listę w normalny sposób, skutecznie nic nie robiąc. Nadal jednak można sprawdzić, czy zwracana wartość to obiekt zerowy (np. Pusta lista) i reagować inaczej w razie potrzeby.

komar
źródło
3

Powinieneś, IMHO, zwrócić PUSTĄ wartość. Nie wiem o C #, ale w Javie mamy to:

  List list = Collections.EMPTY_LIST;
  Set set = Collections.EMPTY_SET;
  Map map = Collections.EMPTY_MAP;

  // For the type-safe 
  List<String> s = Collections.emptyList();
  Set<Long> l = Collections.emptySet();
  Map<Date> d = Collections.emptyMap();

http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html

Marcelo Assis
źródło
1
Odpowiednikiem C # jest Enumerable.Empty<T>(), który zwraca pusty IEnumerable<T>(patrz msdn.microsoft.com/en-us/library/bb341042.aspx )
Avner Shahar-Kashtan
1
Aktualne dokumenty znajdują się tutaj: docs.oracle.com/javase/7/docs/api/java/util/Collections.html - Dokumenty 1.4.2 mają teraz około dziesięciu lat.
Stuart Marks
2

Zwrócę pustą kolekcję po zwróceniu wartości pustej, ponieważ w ten sposób można uniknąć wpisania pustej weryfikacji w kodzie wywołującym.


źródło
2

Dwa rozwiązania oznaczają różne rzeczy.

Jeśli zwracasz tylko zero, ZAWSZE zwracasz pustą kolekcję! Weźmy przykład z katalogu. Jeśli w katalogu nie ma żadnych plików, zwracana jest pusta kolekcja plików.

Z drugiej strony, jeśli katalog nie istnieje, nie jest to właściwe. „Nic nie mogę zwrócić” oznacza coś zupełnie innego. W takim przypadku powinieneś zwrócić wartość null lub zgłosić wyjątek w zależności od sytuacji, nie zwracaj tylko pustej kolekcji, jakby nic nie było nie tak.

Bill K.
źródło
Bardzo rozsądne wyjaśnienie. Zwrócony wynik nie powinien obejmować nieprawidłowego stanu, mamy do tego wyjątki.
Ivaylo Slavov