Szukam rekomendacji tutaj. Mam problem z tym, czy lepiej zwrócić NULL, czy pustą wartość z metody, gdy zwracana wartość nie jest obecna lub nie można jej ustalić.
Jako przykład weź następujące dwie metody:
string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID) // finds a Person with a matching personID.
W ReverseString()
, powiedziałbym, że return pusty łańcuch, ponieważ typem zwracanym jest string, więc osoba dzwoniąca oczekuje tego. W ten sposób osoba dzwoniąca nie będzie musiała sprawdzać, czy zwrócono NULL.
W przypadku FindPerson()
powrotu wartości NULL wydaje się lepsze dopasowanie. Niezależnie od tego, czy new Person()
zwracany jest NULL, czy pusty Obiekt Person ( ), osoba dzwoniąca musi sprawdzić, czy Obiekt Person ma wartość NULL lub jest pusta, zanim cokolwiek z tym zrobi (np. Wywołanie UpdateName()
). Dlaczego więc nie po prostu zwrócić tutaj NULL, a wtedy dzwoniący musi tylko sprawdzić, czy NULL.
Czy ktoś ma z tym problem? Każda pomoc lub wgląd są mile widziane.
Odpowiedzi:
StackOverflow ma dobrą dyskusję na ten właśnie temat w tym pytaniu i odpowiedziach . W najwyżej ocenianym pytaniu Kronoz zauważa:
Osobiście lubię zwracać puste ciągi dla funkcji, które zwracają ciągi, aby zminimalizować ilość błędów, które należy wprowadzić. Musisz jednak upewnić się, że grupa, z którą pracujesz, będzie przestrzegać tej samej konwencji - w przeciwnym razie korzyści wynikające z tej decyzji nie zostaną osiągnięte.
Jednak, jak zaznaczono w plakacie w odpowiedzi SO, wartości zerowe powinny być prawdopodobnie zwrócone, jeśli obiekt jest oczekiwany, aby nie było wątpliwości, czy dane są zwracane.
W końcu nie ma jednego najlepszego sposobu na robienie rzeczy. Budowanie konsensusu zespołu ostatecznie doprowadzi do najlepszych praktyk zespołu.
źródło
for x in list_of_things() {...}
jest prawdopodobnie szybszy do zdobycial = list_of_things(); if l != null {...}
emit(user.hometown)
niżif user.hometown == null { emit("") else {emit(user.hometown)}
W całym kodzie, który piszę, unikam powrotu
null
z funkcji. Przeczytałem to w Clean Code .Problem z używaniem
null
polega na tym, że osoba korzystająca z interfejsu nie wie, czynull
jest to możliwy wynik i czy musi to sprawdzić, ponieważ nie manot null
typu odwołania.W F # można zwracać
option
typ, który może byćsome(Person)
albonone
, więc to oczywiste dla osoby dzwoniącej, że trzeba sprawdzić.Analogiczny wzorzec C # (anty-) to
Try...
metoda:Teraz wiem, że ludzie powiedzieli, że nienawidzą tego
Try...
wzorca, ponieważ parametr wyjściowy łamie idee czystej funkcji, ale tak naprawdę nie różni się od:... i szczerze mówiąc, możesz założyć, że każdy programista .NET wie o
Try...
wzorcu, ponieważ jest on używany wewnętrznie przez platformę .NET. Oznacza to, że nie muszą czytać dokumentacji, aby zrozumieć, co ona robi, co jest dla mnie ważniejsze niż trzymanie się purystycznego poglądu na funkcje (rozumienie, żeresult
jest toout
parametr, a nieref
parametr).Więc poszedłbym z tym,
TryFindPerson
ponieważ wydaje ci się, że to zupełnie normalne, że nie można go znaleźć.Jeśli z drugiej strony nie ma logicznego powodu, że dzwoniący dostarczyłby taki,
personId
który nie istniał, prawdopodobnie zrobiłbym to:... a potem rzuciłbym wyjątek, jeśli byłby nieważny.
Get...
Prefiks oznacza, że rozmówca wie, powinien on odnieść sukces.źródło
Możesz wypróbować wzór specjalnej skrzynki Martina Fowlera z Paterns of Enterprise Application Architecture :
źródło
Myślałbym,
ReverseString()
że zwróci odwrócony ciąg i wyrzuci,IllegalArgumentException
jeśli zostanie przekazany wNull
.Myślę, że
FindPerson()
powinieneś podążać za wzorcem NullObject lub zgłosić niezaznaczony wyjątek dotyczący nie znajdowania czegoś, jeśli zawsze będziesz w stanie coś znaleźć.Radzenie sobie z
Null
czymś jest czymś, czego należy unikać. Posługiwanie sięNull
językami zostało nazwane przez wynalazcę błędem w miliard dolarów.źródło
Zwróć opcję . Wszystkie zalety zwracania innej niepoprawnej wartości (np. Możliwość posiadania pustych wartości w twoich kolekcjach) bez żadnego ryzyka NullPointerException.
źródło
boost::optional<T>
Widzę obie strony tego argumentu i zdaję sobie sprawę, że niektóre dość wpływowe głosy (np. Fowler) opowiadają się za nie zwracaniem wartości zerowych w celu utrzymania kodu w czystości, unikania dodatkowych bloków obsługi błędów itp.
Mam jednak tendencję do popierania zwolenników powrotu wartości zerowej. Uważam, że istnieje istotna różnica w wywoływaniu metody, która odpowiada : nie mam żadnych danych i odpowiada : mam ten pusty ciąg .
Ponieważ widziałem niektóre dyskusje odnoszące się do klasy Person, rozważ scenariusz, w którym próbujesz wyszukać instancję klasy. Jeśli przekażesz jakiś atrybut wyszukiwarki (np. Identyfikator), klient może natychmiast sprawdzić wartość null, aby sprawdzić, czy nie znaleziono żadnej wartości. Niekoniecznie jest to wyjątkowe (stąd nie wymagają wyjątków), ale należy je również wyraźnie udokumentować. Tak, wymaga to pewnego rygoru ze strony klienta i nie, nie sądzę, że to wcale źle.
Teraz rozważ alternatywę, w której zwracasz prawidłowy obiekt Person ... który nie ma w nim nic. Czy wstawiasz null we wszystkich jego wartościach (nazwa, adres, ulubionyDrink), czy też zapełniasz te prawidłowe, ale puste obiekty? W jaki sposób Twój klient ustala, że nie znaleziono faktycznej osoby? Czy muszą sprawdzić, czy nazwa jest pustym ciągiem zamiast null? Czy tego rodzaju rzeczy nie doprowadzą do tak dużo lub więcej bałaganu kodu i instrukcji warunkowych, niż gdybyśmy po prostu sprawdzili, czy nie ma wartości, i ruszyliśmy dalej?
Znów są punkty po obu stronach tego argumentu, z którymi mógłbym się zgodzić, ale uważam, że jest to najbardziej sensowne dla większości ludzi (czyniąc kod łatwiejszym do utrzymania).
źródło
FindPerson
bądźGetPerson
zwróci null lub wyjątek, jeśli klucz nie istnieje?” Jako użytkownik interfejsu API po prostu nie wiem i muszę sprawdzić dokumentację. Z drugiej stronybool TryGetPerson(Guid key, out Person person)
nie wymaga ode mnie sprawdzania dokumentacji i wiem, że nie zgłoszono wyjątku, co stanowi raczej nietypową okoliczność. O to właśnie staram się powiedzieć w mojej odpowiedzi.findPerson
), to absolutnie normalne, że nie ma wyników. Nie powinno to prowadzić do wyjątku.Zwróć null, jeśli chcesz wiedzieć, czy element istnieje, czy nie. W przeciwnym razie zwróć oczekiwany typ danych. Jest to szczególnie prawdziwe, jeśli zwracasz listę przedmiotów. Zazwyczaj można bezpiecznie założyć, że jeśli dzwoniący chce listy, będzie chciał iterować po liście. Wiele (większość? Wszystkich?) Języków kończy się niepowodzeniem, jeśli próbujesz wykonać iterację po wartości null, ale nie podczas iteracji po liście, która jest pusta.
Frustrowanie mnie przy użyciu takiego kodu jest dla mnie frustrujące:
Nie powinienem dodawać specjalnego przypadku do sprawy, gdy nie ma żadnych rzeczy .
źródło
To zależy od semantyki metody. Możesz określić, że Twoja metoda akceptuje tylko „Nie null”. W Javie możesz zadeklarować, że używając adnotacji metadanych:
Powinieneś jakoś określić wartość zwracaną. Jeśli zadeklarujesz, akceptujesz tylko wartości NotNull, jesteś zobowiązany do zwrócenia pustego ciągu (jeśli wejściowy również był pusty).
Drugi przypadek jest nieco skomplikowany pod względem semantyki. Jeśli ta metoda ma zwrócić jakąś osobę według jej klucza podstawowego (i oczekuje się, że ta osoba istnieje), lepszym sposobem jest zgłoszenie wyjątku.
Jeśli szukasz osoby według jakiegoś odgadłego identyfikatora (co wydaje mi się dziwne), lepiej zadeklaruj tę metodę jako @Nullable.
źródło
Gdy tylko jest to możliwe, polecam stosowanie wzorca Null Object. Upraszcza kod podczas wywoływania metod i nie można czytać brzydkiego kodu, takiego jak
if (someObject != null && someObject.someMethod () != whateverValue)
Na przykład, jeśli metoda zwraca kolekcję obiektów, które pasują do określonego wzorca, wówczas zwrócenie pustej kolekcji ma większy sens niż powrót
null
, a iteracja po tej pustej kolekcji nie będzie miała praktycznie żadnego wpływu na wydajność. Innym przypadkiem byłaby metoda, która zwraca instancję klasy używaną do rejestrowania danych, zwracając raczej obiekt Null, niżnull
jest moim zdaniem preferowana, ponieważ nie zmusza użytkowników do zawsze sprawdzania, czy zwrócone odwołanie jestnull
.W przypadkach, w których zwracanie
null
ma sens (findPerson ()
na przykład wywołanie metody), postaram się przynajmniej podać metodę, która zwraca, jeśli obiekt jest obecny (personExists (int personId)
na przykład), innym przykładem może byćcontainsKey ()
metodaMap
w Javie). Czyści kod dzwoniącego, ponieważ łatwo można zauważyć, że istnieje możliwość, że żądany obiekt może nie być dostępny (osoba nie istnieje, klucz nie jest obecny na mapie).null
Moim zdaniem ciągłe sprawdzanie, czy odwołanie zaciemnia kod.źródło
wartość null jest najlepszym rozwiązaniem, jeśli jest zwracana tylko wtedy, gdy spełnione są następujące warunki:
Ponadto upewnij się, że udokumentujesz fakt, że funkcja może zwrócić wartość null.
Jeśli zastosujesz się do tych zasad, wartości zerowe są w większości nieszkodliwe. Zauważ, że jeśli zapomnisz sprawdzić, czy nie ma wartości null, zwykle natychmiast otrzymasz NullPointerException, co zwykle jest dość łatwym błędem do naprawienia. To szybkie podejście do awarii jest znacznie lepsze niż posiadanie fałszywej wartości zwracanej (np. Pusty ciąg), która jest cicho propagowana w twoim systemie, prawdopodobnie uszkadzając dane, bez zgłaszania wyjątku.
Wreszcie, jeśli zastosujesz te reguły do dwóch funkcji wymienionych w pytaniu:
źródło
Moim zdaniem istnieje różnica między zwracaniem wartości NULL, zwracaniem jakiegoś pustego wyniku (np. Pustego ciągu lub pustej listy) a zgłaszaniem wyjątku.
Zwykle stosuję następujące podejście. Uważam wywołanie funkcji lub metody f (v1, ..., vn) za zastosowanie funkcji
gdzie S it „stan świata” T1,…, Tn są typami parametrów wejściowych, a T to typ zwracany.
Najpierw próbuję zdefiniować tę funkcję. Jeśli funkcja jest częściowa (tzn. Istnieją pewne wartości wejściowe, dla których nie jest zdefiniowana) zwracam NULL, aby to zasygnalizować. Jest tak, ponieważ chcę, aby obliczenia zakończyły się normalnie i powiedziały mi, że żądana funkcja nie jest zdefiniowana na danych wejściowych. Używanie np. Pustego ciągu jako wartości zwracanej jest niejednoznaczne, ponieważ może się zdarzyć, że funkcja jest zdefiniowana na wejściach, a pusty ciąg jest poprawnym wynikiem.
Myślę, że dodatkowe sprawdzenie wskaźnika NULL w kodzie wywołującym jest konieczne, ponieważ stosujesz funkcję częściową, a zadaniem wywoływanej metody jest sprawdzenie , czy funkcja nie jest zdefiniowana dla podanego wejścia.
Wolę używać wyjątków dla błędów, które nie pozwalają na przeprowadzenie obliczeń (tzn. Nie było możliwe znalezienie żadnej odpowiedzi).
Załóżmy na przykład, że mam klienta klasy i chcę zaimplementować metodę
aby wyszukać klienta w bazie danych aplikacji według jego kodu. W tej metodzie zrobiłbym to
Dodatkowe kontrole zerowe, np
są częścią semantyki tego, co robię i nie chciałbym ich po prostu „pomijać”, aby poprawić odczyt kodu. Nie sądzę, że dobrą praktyką jest upraszczanie semantyki problemu, aby uprościć kod.
Oczywiście, ponieważ sprawdzanie wartości null występuje bardzo często, dobrze jest, jeśli język obsługuje dla niego specjalną składnię.
Zastanowiłbym się również nad użyciem wzorca Null Object (jak sugeruje Laf), o ile mogę odróżnić obiekt null klasy od wszystkich innych obiektów.
źródło
Metody zwracające kolekcje powinny zwracać puste, ale inne mogą zwracać wartość null, ponieważ w przypadku kolekcji może się tak zdarzyć, że będziesz mieć obiekt, ale nie będzie w nim żadnych elementów, więc obiekt wywołujący zweryfikuje tylko rozmiar.
źródło
Dla mnie są tutaj dwie sprawy. Jeśli zwracasz jakąś listę, zawsze powinieneś ją zwrócić bez względu na wszystko, pustą, jeśli nie masz na niej nic do wstawienia.
Jedynym przypadkiem, w którym widzę jakąkolwiek debatę, jest zwrot jednego elementu. W takiej sytuacji wolę zwracać wartość zerową w przypadku niepowodzenia w takiej sytuacji, ponieważ powoduje to szybkie zawodzenie.
Jeśli zwrócisz obiekt zerowy, a obiekt wywołujący potrzebuje rzeczywistego obiektu, może on spróbować i użyć obiektu zerowego, powodując nieoczekiwane zachowanie. Myślę, że lepiej jest, aby rutyna zaczęła boom, jeśli zapomną poradzić sobie z sytuacją. Jeśli coś jest nie tak, jak najszybciej chcę wyjątek. Mniej prawdopodobne jest, że w ten sposób wyślesz błąd.
źródło
Dodając do tego, co ludzie mówili o
Maybe
konstruktorze typów:Kolejnym dużym zyskiem, poza tym, że nie ryzykuje się NPE, jest semantyczny. W świecie funkcjonalnym, w którym mamy do czynienia z funkcjami czystymi, lubimy tworzyć funkcje, które po zastosowaniu są dokładnie równoważne ich wartości zwracanej . Rozważ przypadek
String.reverse()
: Jest to funkcja, której dany ciąg reprezentuje odwróconą wersję tego ciągu. Wiemy, że ten ciąg istnieje, ponieważ każdy ciąg można odwrócić (ciągi są w zasadzie uporządkowanymi zestawami znaków).A teraz co
findCustomer(int id)
? Ta funkcja reprezentuje klienta, który ma podany identyfikator. Jest to coś, co może istnieć lub nie. Pamiętaj jednak, że funkcje mają jedną zwracaną wartość, więc nie można tak naprawdę powiedzieć „ta funkcja zwraca klienta o podanym identyfikatorze LUB zwracanull
.To jest mój główny problem zarówno ze zwracaniem, jak
null
i zwracaniem pustego obiektu. Wprowadzają w błąd.null
nie jest klientem i nie jest tak naprawdę „nieobecnością klienta”. To brak NIC. Jest to po prostu typ bez wskaźnika do NIC. Bardzo niski poziom, bardzo brzydka i podatna na błędy. Wydaje mi się, że tutaj również jest pusty obiekt. Klient zerowy JEST klientem. To nie jest brak jednego, to nie klient z prośbą o identyfikator, więc zwrot jest po prostu NIEPRAWIDŁOWY. To NIE jest klient, o którego prosić, ale nadal twierdzisz, że zwróciłeś klienta. Ma zły identyfikator, najwyraźniej jest to błąd. Łamie kontrakt sugerowany przez nazwę i podpis metody. Wymaga to, aby kod klienta zakładał pewne rzeczy o twoim projekcie lub czytał dokumentację.Oba te problemy rozwiązuje
Maybe Customer
. Nagle nie mówimy „ta funkcja reprezentuje klienta o podanym identyfikatorze”. Mówimy „ta funkcja reprezentuje klienta o podanym identyfikatorze, jeśli istnieje . Jest teraz bardzo jasny i wyraźny na temat tego, co robi. Jeśli poprosisz o klienta z nieistniejącym identyfikatorem, otrzymaszNothing
. Jak to jest lepsze? niżnull
? Nie tylko z powodu ryzyka NPE, ale także dlatego, że rodzajNothing
nie jest po prostuMaybe
. JestMaybe Customer
. Ma znaczenie semantyczne. W szczególności oznacza „brak klienta”, co oznacza, że taki klient nie istnieje.Innym problemem z zerowym jest oczywiście to, że jest niejednoznaczny. Czy to dlatego, że nie znalazłeś klienta, czy wystąpił błąd połączenia z bazą danych, czy po prostu nie mogę go zobaczyć?
Jeśli masz kilka takich błędów do obsłużenia, możesz albo zgłosić wyjątki dla tych wersji, albo (i ja wolę w ten sposób) możesz zwrócić an
Either CustomerLoadError Customer
, reprezentujący coś, co poszło nie tak, lub zwrócony klient. Ma wszystkie zaletyMaybe Customer
, pozwalając jednocześnie określić, co może pójść nie tak.Najważniejsze, czego szukam, to uchwycenie kontraktu funkcji w jej podpisie. Nie zakładaj rzeczy, nie polegaj na ulotnych konwencjach, bądź wyraźny.
źródło
1) Jeśli semantyczna funkcji jest to, że nic nie może zwrócić, wywołujący MUSI ją przetestować, w przeciwnym razie np. weź swoje pieniądze i nie daj nikomu.
2) Dobrze jest tworzyć funkcje, które semantycznie zawsze coś zwracają (lub rzucają).
W przypadku programu rejestrującego warto zwrócić program rejestrujący, który nie rejestruje się, jeśli nie zdefiniowano żadnego programu rejestrującego. Zwracając kolekcję, prawie nigdy nie ma sensu (z wyjątkiem „wystarczająco niskiego poziomu”, gdzie sama kolekcja to dane, a nie to, co zawiera), aby niczego nie zwracać, ponieważ pusty zestaw jest zbiorem, a nie niczym.
Z osoba przykład pójdę „hibernacji” sposób (dostać vs obciążeniu, IIRC, ale to komplikuje obiektów proxy dla leniwego ładowania) posiadania dwie funkcje, jedną, która zwraca wartość null a po drugie, że rzuca.
źródło
Wartość NULL powinna zostać zwrócona, jeśli aplikacja oczekuje, że dane będą dostępne, ale dane nie są dostępne. Na przykład usługa, która zwraca CITY na podstawie kodu pocztowego, powinna zwrócić wartość null, jeśli miasto nie zostanie znalezione. Dzwoniący może następnie zdecydować się na zerowanie lub wysadzenie w powietrze.
Pusta lista powinna zostać zwrócona, jeśli istnieją dwie możliwości. Dostępne dane lub brak dostępnych danych. Na przykład usługa, która zwraca CITY (s), jeśli populacja jest większa niż pewna liczba. Może zwrócić pustą listę, jeśli Brak danych spełniających podane kryteria.
źródło
Returning NULL to okropny projekt w świecie zorientowanym obiektowo. Krótko mówiąc, użycie NULL prowadzi do:
Sprawdź ten post na blogu, aby uzyskać szczegółowe wyjaśnienie: http://www.yegor256.com/2014/05/13/why-null-is-bad.html
źródło