Sì, ci sono buone ragioni:
- Identifica esattamente ciò che è nullo, che potrebbe non essere ovvio da un file
NullReferenceException
- Fa fallire il codice su input non valido anche se qualche altra condizione significa che il valore non è dereferenziato
- Fa sì che l'eccezione si verifichi prima che il metodo possa avere altri effetti collaterali che potresti raggiungere prima della prima dereferenziazione
- Significa che puoi essere sicuro che se passi il parametro a qualcos'altro, non stai violando il loro contratto
- Documenta i requisiti del tuo metodo (usare Code Contracts è ancora meglio per quello, ovviamente)
Ora per quanto riguarda le tue obiezioni:
- È più lento : hai scoperto che questo è effettivamente il collo di bottiglia nel tuo codice o stai indovinando? I controlli di nullità sono molto rapidi e nella stragrande maggioranza dei casi non saranno il collo di bottiglia
- Rende il codice più difficile da mantenere : penso il contrario. Penso che sia più facile usare il codice in cui è chiarito se un parametro può essere nullo o meno e dove sei sicuro che tale condizione sia applicata.
E per la tua affermazione:
Ovviamente, il codice che usa s genererà comunque un'eccezione.
Veramente? Ritenere:
void f(SomeType s)
{
Console.WriteLine("I've got a message of {0}", s);
}
Questo usa s, ma non genera un'eccezione. Se non è valido per sessere nullo, e questo indica che qualcosa non va, un'eccezione è il comportamento più appropriato qui.
Ora dove metti quei controlli di convalida degli argomenti è una questione diversa. Puoi decidere di fidarti di tutto il codice all'interno della tua classe, quindi non preoccuparti dei metodi privati. Potresti decidere di fidarti del resto del tuo assembly, quindi non preoccuparti dei metodi interni. Quasi certamente dovresti convalidare gli argomenti a favore dei metodi pubblici.
Una nota a margine: l'overload del costruttore a parametro singolo di ArgumentNullExceptiondovrebbe essere solo il nome del parametro, quindi il tuo test dovrebbe essere:
if (s == null)
{
throw new ArgumentNullException("s");
}
In alternativa puoi creare un metodo di estensione, consentendo a un po 'più terser:
s.ThrowIfNull("s");
Nella mia versione del metodo di estensione (generico), faccio in modo che restituisca il valore originale se non è nullo, permettendoti di scrivere cose come:
this.name = name.ThrowIfNull("name");
Puoi anche avere un sovraccarico che non prende il nome del parametro, se non te ne preoccupi troppo.