Lanciare un'eccezione da un modulo di proprietà non è valido?


15

Sono sempre stato dell'idea che le proprietà (cioè le loro operazioni set / get) dovrebbero essere veloci / immediate e senza errori. Non dovresti mai provare / catturare in giro per ottenere o impostare una proprietà.

Ma sto cercando alcuni modi per applicare la sicurezza basata sui ruoli sulle proprietà di alcuni oggetti. Ad esempio una proprietà Employee.Salary. Alcune delle soluzioni che ho incontrato che altri hanno provato (uno in particolare è l'esempio AOP qui ) prevedono il lancio di un'eccezione se l'accessor non ha le autorizzazioni giuste - ma questo va contro una regola personale che ho avuto da molto tempo ormai.

Quindi chiedo: sbaglio? Le cose sono cambiate? È stato accettato che le proprietà dovrebbero essere in grado di generare eccezioni?


1
La stessa domanda su StackOverflow ha suscitato maggiore attenzione e risposte migliori.
Roman Starkov,

Risposte:


14

quando si imposta il valore di una proprietà, va bene generare un'eccezione su un valore non valido

ottenere il valore di una proprietà non dovrebbe (quasi) mai generare un'eccezione

per l'accesso basato sui ruoli, utilizzare interfacce o facciate diverse / più stupide; non lasciare che le persone vedano cose che non possono avere!


5

Lo considero per lo più in cattiva forma, ma anche Microsoft consiglia di utilizzare eccezioni a volte. Un buon esempio è la proprietà astratta Stream.Length . Seguendo le linee guida, sarei più preoccupato di evitare gli effetti collaterali sui vincitori e di limitare gli effetti collaterali sui setter.


4

Direi sicuramente che c'è un difetto nel design se senti la necessità di gettare eccezioni da un setter o getter di proprietà.

Una proprietà è un'astrazione che rappresenta qualcosa che è solo un valore . E dovresti essere in grado di impostare un valore senza temere che ciò possa generare un'eccezione. *

Se l'impostazione della proprietà produce un effetto collaterale, quello dovrebbe invece essere implementato come metodo. E se non produce alcun effetto collaterale, non dovrebbe essere generata alcuna eccezione.

Un esempio già menzionato in una risposta diversa è la Stream.Positionproprietà. Ciò produce effetti collaterali e può generare eccezioni. Ma questo setter proprietà è fondamentalmente solo un wrapper Stream.Seekche potresti chiamare invece.

Personalmente, credo che la posizione non avrebbe dovuto essere una proprietà scrivibile.

Un altro esempio in cui potresti essere tentato di lanciare un'eccezione da un setter proprietà è nella convalida dei dati:

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

Ma esiste una soluzione migliore a questo problema. Introdurre un tipo che rappresenti un indirizzo email valido:

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

La Emailclasse garantisce che non può contenere un valore che non sia un indirizzo e-mail valido e che le classi che devono archiviare le e-mail sono esonerate dal dovere di convalidarle.

Ciò porta anche a una maggiore coesione (un indicatore di una buona progettazione del software): la conoscenza di ciò che è un indirizzo e-mail e di come viene convalidato esiste solo nella Emailclasse, che ha solo quella preoccupazione.

* ObjectDisposedException è l'unica eccezione valida (nessun gioco di parole intenzionale) a cui riesco a pensare al momento.


2

So che la tua domanda è specifica per .NET , ma dal momento che C # condivide un po 'di storia con Java, ho pensato che potresti essere interessato. Sono Non in alcun modo implica che perché qualcosa è fatto in Java, dovrebbe essere fatto in C #. So che i due sono molto diversi, soprattutto in quanto C # ha un supporto linguistico molto migliorato per le proprietà. Sto solo dando un po 'di contesto e prospettiva.

Dalla specifica JavaBeans :

Proprietà vincolate A volte, quando si verifica una modifica della proprietà, alcuni altri bean potrebbero voler convalidare la modifica e rifiutarla se non è appropriata. Ci riferiamo alle proprietà che subiscono questo tipo di controllo come proprietà vincolate. In Java Beans, per supportare PropertyVetoException sono richiesti metodi di setter proprietà vincolati. Questo documento agli utenti della proprietà vincolata che ha tentato di aggiornare può essere posto il veto. Quindi una semplice proprietà vincolata potrebbe apparire come:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Prendi tutto questo con un granello di sale, per favore. Le specifiche JavaBeans sono vecchie e le proprietà C # sono (IMO) un enorme miglioramento rispetto alle proprietà basate sulla "convenzione di denominazione" di Java. Sto solo cercando di dare un piccolo contesto, tutto qui!


È un buon punto, ma la distinzione tra un metodo setter e ac # property setter è abbastanza significativa da essere un caso diverso per me. Allo stesso modo, le eccezioni verificate di Java costringono il codice chiamante a essere consapevole del fatto che può essere generata un'eccezione, quindi ci sono meno possibilità che questo possa sorprendere chiunque.
Steven Evers,

2

Il punto di una proprietà è il Principio di accesso uniforme , vale a dire che un valore dovrebbe essere accessibile attraverso la stessa interfaccia sia implementato dalla memoria che dal calcolo. Se la tua proprietà genera un'eccezione che rappresenta una condizione di errore al di fuori del controllo del programmatore, il tipo che dovrebbe essere catturato e gestito, stai forzando il tuo client a sapere che il valore è ottenuto tramite calcolo.

D'altra parte, non vedo alcun problema con l'utilizzo di assertori o eccezioni simili a asserzioni che hanno lo scopo di segnalare al programmatore un uso errato dell'API piuttosto che essere catturati e gestiti. In questi casi, la risposta corretta dal punto di vista dell'utente dell'API non è quella di gestire l'eccezione (quindi preoccuparsi implicitamente se il valore viene ottenuto tramite calcolo o archiviazione). Serve per correggere il suo codice in modo che l'oggetto non finisca in uno stato non valido o per farti correggere il tuo codice in modo che l'asserzione non si attivi.


Non vedo come segue "stai forzando il tuo cliente a sapere che il valore è ottenuto tramite il calcolo". Certamente anche l'accesso all'archiviazione può costituire un'eccezione. In effetti, come definiresti la differenza tra i due?
Timwi,

Credo che la distinzione che si sta facendo sia che il semplice recupero di un valore da un campo (archiviazione) e il fare qualcosa di innocuo con esso, come metterlo in una variabile di un tipo appropriato (cioè senza il cast), non può generare un'eccezione. In tal caso, un'eccezione potrebbe verificarsi solo se successivamente hai fatto qualcosa con il valore. Se viene generata un'eccezione in un getter di proprietà, in quel caso semplicemente l'atto di recuperare il valore e posizionarlo in una variabile potrebbe causare un'eccezione.
Apprendista Dr. Wily,

1

AFAIK, questa guida deriva principalmente dal processo di pensiero secondo cui le proprietà possono essere utilizzate al momento della progettazione . (Ad esempio la proprietà Text su un TextBox) Se la proprietà genera un'eccezione quando un designer sta tentando di accedervi, VS non avrà una buona giornata. Otterrai un sacco di errori solo provando a usare il designer e per i designer dell'interfaccia utente, non verranno visualizzati. Si applica anche al tempo di debug, sebbene ciò che vedrai in uno scenario di eccezione sia solo "xxx Exception" e non ostruirà VS IIRC.

Per i POCO, non farà davvero del male, ma mi rifaccio ancora dal farlo da solo. Immagino che le persone vorranno accedere alle proprietà più frequentemente, quindi dovrebbero normalmente essere a basso costo. Le proprietà non dovrebbero fare il lavoro con i metodi, dovrebbero semplicemente ottenere / impostare alcune informazioni e farle come regola generale.


0

Anche se molto dipenderà dal contesto, in genere eviterei di inserire qualsiasi tipo di logica fragile (comprese le eccezioni) nelle proprietà. Proprio come un esempio, ci sono molte cose che tu (o qualche libreria che stai usando) potresti fare che usano la riflessione per scorrere le proprietà con conseguente errori che non ti aspettavi in ​​fase di progettazione.

Dico in generale, però, poiché in alcune occasioni potresti voler assolutamente bloccare qualcosa senza preoccuparti troppo delle conseguenze. La sicurezza immagino che sarebbe il caso classico lì.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.