Poprawnie używając null
Istnieją różne sposoby korzystania null. Najczęstszym i semantycznie poprawnym sposobem jest użycie go, gdy możesz mieć lub nie mieć jednej wartości. W tym przypadku wartość jest równa nulllub jest czymś znaczącym, jak zapis z bazy danych lub coś takiego.
W takich sytuacjach najczęściej używasz go w następujący sposób (w pseudokodzie):
if (value is null) {
doSomethingAboutIt();
return;
}
doSomethingUseful(value);
Problem
I ma bardzo duży problem. Problem polega na tym, że do czasu wywołania doSomethingUsefulwartość mogła nie zostać sprawdzona null! Jeśli tak nie było, program najprawdopodobniej ulegnie awarii. A użytkownik może nawet nie zobaczyć żadnych dobrych komunikatów o błędach, pozostawiając coś w rodzaju „strasznego błędu: pożądana wartość, ale dostała zero!” (po aktualizacji: chociaż może występować jeszcze mniej informacji, takich jak Segmentation fault. Core dumped.lub, co gorsza, brak błędów i nieprawidłowe manipulowanie wartością NULL w niektórych przypadkach)
Niezwykle częstym błędem jest zapominanie o pisaniu czeków nulli radzeniu sobie w nullsytuacjach . Właśnie dlatego Tony Hoare, który wynalazł, powiedział podczas konferencji programowej QCon London w 2009 roku, że popełnił błąd miliarda dolarów w 1965 roku:
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Błąd-Tony-Hoarenull
Unikanie problemu
Niektóre technologie i języki sprawiają, że sprawdzanie pod kątem nullniemożności zapomnienia na różne sposoby, zmniejsza liczbę błędów.
Na przykład Haskell ma Maybemonadę zamiast zer. Załóżmy, że DatabaseRecordjest to typ zdefiniowany przez użytkownika. W Haskell wartość typu Maybe DatabaseRecordmoże być równa Just <somevalue>lub może być równa Nothing. Możesz następnie używać go na różne sposoby, ale bez względu na to, jak go używasz, nie możesz zastosować niektórych operacji Nothingbez wiedzy o tym.
Na przykład ta funkcja o nazwie zeroAsDefaultzwraca xdla Just xi 0dla Nothing:
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
Christian Hackl mówi, że C ++ 17 i Scala mają swoje własne sposoby. Możesz więc spróbować dowiedzieć się, czy twój język ma coś takiego i użyć go.
Wartości zerowe są nadal w powszechnym użyciu
Jeśli nie masz nic lepszego, używanie nulljest w porządku. Tylko uważaj na to. W każdym razie pomocne będą deklaracje typu w funkcjach.
Może to również brzmieć niezbyt progresywnie, ale powinieneś sprawdzić, czy twoi koledzy chcą z niego skorzystać, nullczy czegoś innego. Mogą być konserwatywne i z pewnych powodów mogą nie chcieć korzystać z nowych struktur danych. Na przykład obsługa starszych wersji języka. Takie rzeczy powinny być zadeklarowane w standardach kodowania projektu i odpowiednio omówione z zespołem.
Na twoją propozycję
Sugerujesz użycie osobnego pola boolowskiego. Ale i tak musisz to sprawdzić i nadal możesz zapomnieć o sprawdzeniu. Więc nic tu nie wygrało. Jeśli możesz nawet zapomnieć o czymś innym, takim jak aktualizacja obu wartości za każdym razem, jest jeszcze gorzej. Jeśli problem zapomnienia o sprawdzeniu nullnie zostanie rozwiązany, nie ma sensu. Unikanie nulljest trudne i nie należy robić tego w sposób, który pogorszy sytuację.
Jak nie używać null
Wreszcie istnieją powszechne sposoby nullnieprawidłowego użycia . Jednym z takich sposobów jest użycie go zamiast pustych struktur danych, takich jak tablice i łańcuchy. Pusta tablica jest właściwą tablicą, jak każda inna! Prawie zawsze jest ważne i przydatne dla struktur danych, które mogą pasować do wielu wartości, aby mogły być puste, tzn. Mają zerową długość.
Z punktu widzenia algebry pusty ciąg znaków dla ciągów jest podobny do 0 dla liczb, tj. Tożsamość:
a+0=a
concat(str, '')=str
Pusty ciąg znaków pozwala ogólnie stać się monoidem:
https://en.wikipedia.org/wiki/Monoid
Jeśli go nie dostaniesz, nie jest to dla ciebie ważne.
Zobaczmy teraz, dlaczego jest to ważne dla programowania w tym przykładzie:
for (element in array) {
doSomething(element);
}
Jeśli podamy tutaj pustą tablicę, kod będzie działał poprawnie. Po prostu nic nie zrobi. Jeśli jednak przejdziemy nulltutaj, prawdopodobnie nastąpi awaria z błędem typu „przepraszam, nie można przepuścić przez zero”. Możemy to owinąć, ifale to jest mniej czyste i znowu, możesz zapomnieć to sprawdzić
Jak obsłużyć zerowy
To, co doSomethingAboutIt()powinno być zrobione, a zwłaszcza czy powinno to spowodować wyjątek, to kolejna skomplikowana kwestia. Krótko mówiąc, zależy to od tego, czy nullbyła to dopuszczalna wartość wejściowa dla danego zadania i czego oczekuje się w odpowiedzi. Wyjątek stanowią zdarzenia, których nie oczekiwano. Nie zajmę się tym tematem. Ta odpowiedź jest już bardzo długa.
std::optionallubOption. W innych językach może być konieczne zbudowanie odpowiedniego mechanizmu lub skorzystanie znullczegoś podobnego, ponieważ jest to bardziej idiomatyczne.