2009-09-28 16 views
39

Nella mia applicazione ho bisogno di lanciare un'eccezione se una proprietà di una classe specifica è nulla o vuota (nel caso si tratti di una stringa). Non sono sicuro di quale sia l'eccezione migliore da utilizzare in questo caso. Non vorrei creare una nuova eccezione e non sono sicuro che ArgumentNullException sia appropriato in questo caso.Quale tipo di eccezione utilizzare quando una proprietà non può essere nullo?

Devo creare una nuova eccezione oppure esiste un'eccezione che posso usare?

Non mi dispiace di lanciare un'eccezione ApplicationException.

+0

Ricordate anche le proprietà sono metodi sintattici zuccherati. – Dykam

+0

Una discussione interessante correlata: http://stackoverflow.com/questions/1488472/best-practices-throwing-exceptions-from-properties – Joren

risposta

65

I MSDN guidelines for standard exceptions stati:

Fare valore d'uso per il nome del parametro valore implicito di immobili in setter.

Il seguente esempio di codice illustra una struttura che genera un'eccezione se il chiamante passa un argomento nullo.

public IPAddress Address 
{ 
    get 
    { 
     return address; 
    } 
    set 
    { 
     if(value == null) 
     { 
      throw new ArgumentNullException("value"); 
     } 
     address = value; 
    } 
} 

Inoltre, il MSDN guidelines for property design dicono:

evitare di gettare eccezioni getter proprietà.

I getter di proprietà devono essere semplici operazioni senza precondizioni. Se un getter potrebbe generare un'eccezione, considerano ridisegnare la proprietà di essere un metodo. Questa raccomandazione non è non applicabile agli indicizzatori. Gli indicizzatori possono eseguire le eccezioni a causa di argomenti non validi .

E 'valida e accettabile per gettare eccezioni da un setter di proprietà.

Quindi buttare ArgumentNullException nel setter sul null, e ArgumentException sulla stringa vuota, e non fare nulla nel getter. Poiché il setter lancia e solo tu hai accesso al campo di supporto, è facile accertarti che non contenga un valore non valido. Avere il lancio getter è quindi inutile. Questo potrebbe tuttavia essere un buon punto per utilizzare Debug.Assert.

Se davvero non si può fornire un valore predefinito appropriato, quindi suppongo avete tre opzioni:

  1. ritorno giusto tutto ciò che è nella proprietà e documentare questo comportamento come parte del contratto di utilizzo. Lascia che sia il chiamante a occuparsene. Potresti anche richiedere un valore valido nel costruttore. Questo potrebbe essere completamente inappropriato per la tua applicazione.

  2. Sostituire la proprietà con metodi: un metodo setter che genera quando viene passato un valore non valido e un metodo getter che genera InvalidOperationException quando alla proprietà non è mai stato assegnato un valore valido.

  3. Throw InvalidOperationException dal getter, come si potrebbe considerare 'proprietà non è mai stato assegnato' uno stato non valido. Mentre non dovresti normalmente buttare da getter, suppongo che questo potrebbe essere un buon motivo per fare un'eccezione.

Se scegli le opzioni 2 o 3, si dovrebbe includere anche un metodo che restituisce un TryGet- bool che indica se la proprietà è stata impostata su un valore valido, e in caso affermativo restituisce tale valore in un parametro out . Altrimenti si costringono i chiamanti a essere pronti a gestire un InvalidOperationException, a meno che non abbiano precedentemente impostato la proprietà da soli e quindi sappiano che non genererà. Confronta int.Parse versus int.TryParse.

Io suggerirei di utilizzare l'opzione 2 con il metodo TryGet. Non viola alcuna linea guida e impone requisiti minimi sul codice chiamante.


Circa gli altri suggerimenti
ApplicationException è troppo generale. ArgumentException è un po 'troppo generico per null, ma va bene altrimenti.MSDN docs again:

gettare la più specifica (la più derivata) eccezione che è appropriata. Ad esempio, se un metodo riceve un argomento null (Nothing in Visual Basic), dovrebbe generare System.ArgumentNullException invece del suo tipo di base System.ArgumentException.

In realtà non si dovrebbe usare ApplicationException a tutti (docs):

Do derivano eccezioni personalizzate dalla T: classe System.Exception piuttosto che il T: class System.ApplicationException.

Inizialmente si pensava che le eccezioni personalizzate dovessero derivare dalla classe ApplicationException; tuttavia, questo non è stato trovato per aggiungere un valore significativo. Per ulteriori informazioni, vedere Best practice per la gestione delle eccezioni.

InvalidOperationException è destinato non per quando gli argomenti di un metodo o una proprietà non sono validi, ma per quando il operazione nel suo complesso è valido (docs). Non deve essere gettato dal setter:

fare un'eccezione System.InvalidOperationException se in uno stato inadeguato. System.InvalidOperationException deve essere generato se un insieme di proprietà o una chiamata al metodo non sono appropriati dato lo stato corrente dell'oggetto. Ad esempio, la scrittura su un System.IO.FileStream che è stato aperto per la lettura dovrebbe generare un'eccezione System.InvalidOperationException.

Incidentalmente, InvalidOperationException è per quando l'operazione non è valida per stato corrente dell'oggetto. Se l'operazione è sempre non valida per l'intera classe, è necessario utilizzare NotSupportedException.

+0

@Joren, @Vadim indica che ha bisogno di un istruttore predefinito e non ha un valore predefinito ragionevole per questo campo, quindi il solo monitoraggio del setter non sarà sufficiente per il suo scenario. – bdukes

+0

Hai ragione, modificherò la mia risposta. – Joren

+0

@ Jimen, ottima risposta. – Vadim

1

Il costruttore lo imposta su un valore non nullo? Se è così vorrei solo lanciare ArgumentNullException dal setter.

4

Vorrei buttare un InvalidOperationException. MSDN dice che "viene generato quando una chiamata al metodo non è valida per lo stato corrente dell'oggetto".

+1

Non penso che sia appropriato per questo scenario. InvalidOperationException deve essere utilizzato solo quando l'oggetto chiamato non è valido. In questo caso l'oggetto chiamato è valido al 100%, è l'argomento del metodo non valido. – JaredPar

+2

Lanciate 'InvalidOperationException' se l'oggetto si trovasse in uno stato tale che non sarebbe affatto valido usare il setter di proprietà. Cioè, * l'operazione * stessa non è valida. I documenti MSDN danno l'esempio di "scrittura su un' System.IO.FileStream' che è stato aperto per la lettura ". – Joren

+0

Suppongo che non conosciamo lo scenario esatto di ciò che viene chiamato. Sto lavorando partendo dal presupposto che stiamo chiamando un metodo di una classe e stiamo provando a usare una proprietà di quella classe all'interno del metodo. In tal caso, l'oggetto non è valido per il metodo che viene chiamato (poiché la proprietà deve essere impostata prima che il metodo possa essere chiamato). Sembra che @JaredPar stia considerando lo scenario in cui una proprietà su un argomento non è inizializzata. In tal caso, il suo suggerimento di "ArgumentException" ha più senso. – bdukes

2

Bene, non è un argomento, se si fa riferimento a una proprietà di una classe. Quindi, non dovresti usare ArgumentException o ArgumentNullException.

NullReferenceException potrebbe accadere se lasci le cose da solo, quindi presumo che non è quello che stai cercando.

Quindi, utilizzare ApplicationExeption o InvalidOperationException sarebbe probabilmente la soluzione migliore, assicurandosi di fornire una stringa significativa per descrivere l'errore.

+1

Sono abbastanza sicuro che ci siano classi in .NET Framework che generano ArgumentException dal setter di una proprietà. Non riesco a ricordare dove esattamente ma ricordo di averlo incontrato. Si potrebbe sostenere che potrebbe non essere "corretto", ma indipendentemente dal framework ha stabilito il precedente. – Tinister

2

È opportuno creare ArgumentNullException se qualcuno tenta di assegnare null.

Una proprietà non deve mai avviare un'operazione di lettura.

+0

Da dove viene l'idea che una proprietà non dovrebbe mai lanciare un'operazione di lettura? Certamente può accadere abbastanza facilmente. –

+1

@ John Non credo che il suo punto fosse che non poteva, ma semplicemente che non dovrebbe. –

+1

@John Fisher: Cwalina, Krzystof e Brad Abrams: "Linee guida per la progettazione di quadri: convenzioni, idiomi e schemi per le librerie .Net riutilizzabili", Addison-Wesley, 2006. p. 121 - tra le altre fonti. –

1

Se il problema è che un membro di un argomento, e non l'argomento stesso, è null, penso che la scelta migliore sia il più generico ArgumentException. ArgumentNullException non funziona qui perché l'argomento non è in realtà null. Invece è necessario il più generico "qualcosa è sbagliato con il tuo argomento" tipo di eccezione.

un messaggio dettagliato per il costruttore sarebbe molto appropriato qui

+0

Cosa intendi per "messaggio di dettaglio per il costruttore"? Devo avere un costruttore predefinito. – Vadim

+0

@Vadim un motivo dettagliato sul motivo per cui l'eccezione argomento viene generata. Ad esempio, "Proprietà Blah del parametro foo deve essere non nullo" – JaredPar

+0

@Vadim, si riferisce al constatore dell'eccezione, non al costruttore dell'oggetto. – bdukes

1

Se non può essere nullo o vuoto, abbiamo il tuo setter non consente valori nulli o vuoti, o gettare un ArgumentException se questo è il caso.

Inoltre, è necessario che la proprietà sia impostata nel costruttore.

In questo modo si impone un valore valido anziché tornare più tardi e si dice che non è possibile determinare il saldo dell'account poiché l'account non è impostato.

Ma, sono d'accordo con la risposta di bduke.

+1

Devo avere un costruttore predefinito. – Vadim

+0

Non è possibile inserire un valore predefinito valido. Lo prendo nel costruttore? –

+1

Sfortunatamente non riesco a giocare a indovinare. Non ho idea di quale valore dovrebbe essere impostato in fase di esecuzione. – Vadim

1

Esiste un precedente per estendere l'interpretazione di ArgumentNullException al significato "l'argomento stringa è nullo o vuoto": System.Windows.Clipboard.SetText genererà un ArgumentNullException in questo caso.

Quindi non vedrei nulla di sbagliato nell'usare questo piuttosto che l'ArgumentException più generale nel setter della proprietà, purché tu lo documenti.

0

Basta lanciare qualsiasi cosa fintanto che il messaggio di errore è utile per uno sviluppatore. Questa classe di eccezione non dovrebbe mai accadere al di fuori dello sviluppo, comunque.

Problemi correlati