Utilizzo corretto di null
Esistono diversi modi di utilizzo null
. Il modo più comune e semanticamente corretto è usarlo quando potresti avere o meno un singolo valore. In questo caso un valore è uguale null
o è qualcosa di significativo come un record dal database o qualcosa del genere.
In queste situazioni, lo usi principalmente in questo modo (in pseudo-codice):
if (value is null) {
doSomethingAboutIt();
return;
}
doSomethingUseful(value);
Problema
E ha un grosso problema. Il problema è che quando invochi doSomethingUseful
il valore potrebbe non essere stato verificato null
! In caso contrario, probabilmente il programma si arresterà in modo anomalo. E l'utente potrebbe anche non vedere alcun buon messaggio di errore, rimanendo con qualcosa come "errore orribile: valore desiderato ma ottenuto nullo!" (dopo l'aggiornamento: anche se Segmentation fault. Core dumped.
in alcuni casi potrebbe esserci un errore informativo ancora minore come , o peggio ancora, nessun errore e manipolazione errata su null)
Dimenticare di scrivere controlli null
e gestire null
situazioni è un bug estremamente comune . Ecco perché Tony Hoare, che ha inventato, ha null
dichiarato in una conferenza sul software chiamata QCon London nel 2009 di aver commesso l'errore di un miliardo di dollari nel 1965:
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- errore-Tony-Hoare
Evitare il problema
Alcune tecnologie e lingue rendono null
impossibile il controllo dell'oblio in diversi modi, riducendo la quantità di bug.
Ad esempio Haskell ha la Maybe
monade invece di null. Supponiamo che DatabaseRecord
sia un tipo definito dall'utente. In Haskell un valore di tipo Maybe DatabaseRecord
può essere uguale Just <somevalue>
o uguale a Nothing
. È quindi possibile utilizzarlo in diversi modi, ma indipendentemente da come lo si utilizza non è possibile applicare alcune operazioni Nothing
senza saperlo.
Ad esempio questa funzione chiamata zeroAsDefault
restituisce x
per Just x
e 0
per Nothing
:
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
Christian Hackl afferma che C ++ 17 e Scala hanno i loro modi. Quindi potresti voler provare a scoprire se la tua lingua ha qualcosa del genere e usarla.
I null sono ancora ampiamente utilizzati
Se non hai niente di meglio, allora usare null
va bene. Continua a fare attenzione. Digitare le dichiarazioni nelle funzioni ti aiuterà comunque in qualche modo.
Inoltre, potrebbe non sembrare molto progressivo, ma dovresti controllare se i tuoi colleghi vogliono usare null
o qualcos'altro. Potrebbero essere prudenti e potrebbero non voler utilizzare nuove strutture dati per alcuni motivi. Ad esempio, supportando versioni precedenti di una lingua. Tali cose dovrebbero essere dichiarate negli standard di codifica del progetto e adeguatamente discusse con il team.
Sulla tua proposta
Suggerisci di usare un campo booleano separato. Ma devi comunque controllarlo e potresti ancora dimenticartene. Quindi non c'è niente vinto qui. Se puoi anche dimenticare qualcos'altro, come aggiornare entrambi i valori ogni volta, allora è anche peggio. Se il problema di dimenticare di verificare null
non è risolto, non ha senso. Evitare null
è difficile e non dovresti farlo in modo da peggiorare le cose.
Come non usare null
Infine, ci sono modi comuni per utilizzare in null
modo errato. Uno di questi è quello di usarlo al posto di strutture di dati vuote come array e stringhe. Un array vuoto è un array adeguato come qualsiasi altro! È quasi sempre importante e utile per le strutture di dati, che possono adattarsi a più valori, per poter essere vuoti, ovvero avere lunghezza 0.
Dal punto di vista dell'algebra una stringa vuota per le stringhe è molto simile a 0 per i numeri, ovvero identità:
a+0=a
concat(str, '')=str
La stringa vuota consente alle stringhe in generale di diventare un monoide:
https://en.wikipedia.org/wiki/Monoid
Se non lo ottieni non è così importante per te.
Ora vediamo perché è importante per la programmazione con questo esempio:
for (element in array) {
doSomething(element);
}
Se passiamo un array vuoto qui il codice funzionerà bene. Non farà proprio niente. Tuttavia, se passiamo un null
qui, probabilmente avremo un arresto anomalo con un errore del tipo "impossibile passare da null a, scusa". Potremmo avvolgerlo if
ma è meno pulito e di nuovo, potresti dimenticare di controllarlo
Come gestire null
Ciò che doSomethingAboutIt()
dovrebbe essere fatto e soprattutto se dovrebbe generare un'eccezione è un altro problema complicato. In breve, dipende dal fatto che null
fosse un valore di input accettabile per una determinata attività e da cosa ci si aspetta in risposta. Le eccezioni sono per eventi che non erano previsti. Non approfondirò questo argomento. Questa risposta è già molto lunga.
std::optional
oOption
. In altre lingue, potresti dover creare tu stesso un meccanismo appropriato, oppure potresti effettivamente ricorrere anull
qualcosa di simile perché è più idiomatico.