Eccezione di lancio da una proprietà quando il mio stato dell'oggetto non è valido [duplicato]

5

Sono consapevole che nel caso generale, la risposta a "dovrei lanciare un'eccezione da una proprietà" è "generalmente no, ma in alcune circostanze speciali è OK farlo". Esiste una linea guida Microsoft che fornisce questa risposta generale e anche un domanda su questo sito.

Ora mi trovo di fronte a una situazione che ritengo possa essere una di quelle "speciali" in cui va bene. La mia domanda: questa è davvero una di quelle situazioni eccezionali in cui l'eccezione è superiore alle alternative? O è meglio usare un getter / setter come nel caso generale?

La mia applicazione utilizza Linq2SQL, e c'è il caso in cui il mio oggetto può essere in uno stato non valido perché qualcuno o qualcosa ha scritto assurdità nel database. Considera questo esempio di giocattolo:

[Table(Name="Rectangle")]
public class Rectangle {

[Column(Name="ID", IsPrimaryKey = true, IsDbGenerated = true)]
public int ID {get; set;} 

[Column(Name="firstSide")]
public double firstSide {get; set;} 

[Column(Name="secondSide")]
public double secondSide {get; set;} 

public double sideRatio 
{
    get
    {
            return firstSide/secondSide; 
    }
}
}

Qui, potrei scrivere codice che garantisce che la mia applicazione non scriva mai un rettangolo con un lato di lunghezza zero nel database. Ma non importa quanto sia antiproiettile creare il mio codice, qualcuno potrebbe aprire il database con un'applicazione diversa e creare un rettangolo non valido, specialmente uno con uno 0 per secondSide. (Per questo esempio, si prega di dimenticare che è possibile progettare il database in modo tale che scrivere una lunghezza laterale di zero nella tabella del rettangolo sia impossibile, il mio modello di dominio è molto complesso e ci sono vincoli sullo stato del modello che non possono essere espressi in un database relazionale).

Quindi, la soluzione a cui sto gravitando è di cambiare il getter in:

get 
{
     if(firstSide > 0 && secondSide > 0) return firstSide/secondSide; 
     else throw new System.InvalidOperationException("All rectangle sides should have a positive length"); 
}

Il ragionamento alla base del non fare eccezioni alle proprietà è che i programmatori dovrebbero essere in grado di usarli senza dover prendere precauzioni su come catturarli e gestirli. Ma in questo caso, penso che sia OK continuare ad usare questa proprietà senza tali precauzioni:

  • se l'eccezione viene generata perché la mia applicazione ha scritto nel database un rettangolo diverso da zero, questo è un bug grave. Non può e non deve essere gestito nell'applicazione, ma dovrebbe esserci un codice che lo impedisca. È bene che l'eccezione sia visibilmente lanciata, perché in questo modo viene catturato il bug.
  • se viene lanciata un'eccezione perché una diversa applicazione modifica i dati nel database, quindi gestirli non rientra nell'ambito della mia applicazione. Quindi non posso farci nulla se lo prendo.

È un ragionamento abbastanza valido per superare la parte "evita" della linea guida e lanciare l'eccezione? O dovrei trasformarlo in un metodo dopotutto? Si noti che nel codice reale, le proprietà che possono avere uno stato non valido si sentono meno come il risultato di un calcolo, quindi sono proprietà "naturali", non metodi.

    
posta Rumi P. 15.10.2013 - 16:57
fonte

3 risposte

2

Dal mio punto di vista questo è totalmente valido. È importante assicurarsi che i valori validi siano impostati, tuttavia se non si è ancora sicuri di poter spostare la logica nel costruttore dell'oggetto (supponendo che ciò si applichi al proprio ambiente)

Controlla anche questo altro post

sta generando un'eccezione un cattivo stato delle proprietà?

    
risposta data 21.10.2013 - 23:59
fonte
1

Puoi scegliere di non lasciare mai che l'applicazione costruisca un'istanza non valida del tuo oggetto.

private double _firstSide;

[Column(Name="firstSide")]
public double FirstSide {
    get { return this._firstSide; }
    set 
    {
        if(value <= 0) throw new System.InvalidOperationException("All rectangle sides should have a positive length");
        this._firstSide = value;
    }
} 

private double _secondSide;

[Column(Name="secondSide")]
public double SecondSide
{
    get { return this._secondSide; }
    set 
    {
        if(value <= 0) throw new System.InvalidOperationException("All rectangle sides should have a positive length");
        this._secondSide = value;
    }
}

public double SideRatio 
{
    get
    {
        return this.FirstSide / this.SecondSide; // safe
    }
}

Gestisci gli errori durante la creazione dell'istanza / l'impostazione delle proprietà. Quando è il momento di fare alcuni algoritmi, puoi tranquillamente accedere a SideRatio senza l'overhead per gestire oggetti strani.

In questo modo, hai una chiara distinzione tra il livello di accesso ai dati che validerà gli oggetti e l'uso di questi oggetti, che sono garantiti come sicuri.

    
risposta data 22.10.2013 - 17:09
fonte
0

Penso che quello che hai in questo esempio sia un metodo e non una proprietà. Quando lo si codifica come metodo, è possibile definire un intervallo di valori di ritorno validi (come nel caso di molti metodi .NET come trovare una stringa all'interno di un'altra, si ottiene -1 se non trovato), in questo modo il codice di richiamo potrebbe determinare il risultato dell'operazione piuttosto facilmente.

Se il tuo esempio presentato è solo per semplificazione, allora il caso generale sarebbe quello di convalidare i dati prima di creare l'oggetto - Questo è parzialmente ciò che i costruttori sono per: Dove mettere la convalida nei modelli di dominio .

Ad ogni modo, immagino che non puoi rendere il tuo oggetto così delicato in modo tale che il chiamante possa usarli in modo abbastanza sicuro senza anticipare la possibilità di un errore. Il tuo doco dovrebbe consigliare all'utente che determinati oggetti siano creati con cura.

    
risposta data 22.10.2013 - 00:24
fonte

Leggi altre domande sui tag