2010-07-24 9 views
6

Vorrei limitare il valore di un parametro numerico in un costruttore all'interno di un determinato intervallo.Limitare il valore di un parametro in un costruttore AT DESIGN TIME

So che il modo convenzionale è quello di fare qualcosa di simile al seguente:

public class Foo 
{ 
    public int MaxAmount { get; } 
    public int Amount { get; set; } 

    public Foo(int amount) 
    { 
     if (amount > MaxAmount) { Amount = MaxAmount; } 
     else if (amount < 1) { Amount = 1; } 
     else { Amount = amount; } 
    } 
} 

Ma quello che non mi piace di questo è che il chiamante non sa quando la proprietà viene impostata su un valore diverso cosa è stato specificato Potrei restituire un'eccezione invece di bloccare silenziosamente il valore, ma non è molto amichevole.

Quello che mi piacerebbe è qualcosa di simile a questo:

public Foo(int(1, this.MaxAmount) amount) // Where int(minimumValue, maximumValue) 
{ 
    Amount = amount; 
}

in cui non si sarebbe nemmeno in grado di istanziare Foo con un valore inaccettabile - il quadro sarebbe impedirlo.

È possibile qualcosa di simile?

EDIT PER CHIAREZZA:

quello che sto cercando è un mezzo attraverso il quale il parametro può trasportare e comunicare le informazioni sui suoi vincoli - in un 'cotto in' modo che potrebbe, ad esempio, superficie in Intellisense quando hai scritto la chiamata. Quindi, eviterei il tentativo di istanziare la classe anche se i valori per i parametri non erano validi.

Se, ad esempio, il programma è in esecuzione e l'utente digita un numero (N) e preme un pulsante che crea un nuovo Foo con una quantità non valida di N, ora ho un'eccezione da gestire e qualcosa da eseguire il debug e risolvere. Perché persino permetterlo in primo luogo? Se Foo è stato definito esplicitamente come avente un limite superiore di 4 per la sua proprietà Amount, qual è il punto di permettere allo sviluppatore di scrivere Foo (5) quando potrei averlo informato che il valore che sta passando non è valido nel momento in cui lui Scritto?

Se c'è dello zucchero sintattico, come ParameterConstraint o qualcosa del genere, che è gestito dal Framework per me, così che non devo girare il mio in ogni classe che scrivo, penso che sarebbe molto utile.

risposta

7

È possibile eseguire questa operazione utilizzando il controllo del contratto statico con Code Contracts (solo Premium - L'edizione standard offre solo il controllo del contratto di runtime).

La sintassi è semplicemente

public Foo(int amount) { 
    Contract.Requires(amount < MaxAmount); 
    ... 
} 

(Richiede) i contratti vengono valutate verificando che gli argomenti è vincolato quando si chiama il metodo. Nella tua istanza, sarà difficile valutare l'argomento del costruttore con il campo di istanza MaxAmount, perché non è possibile verificare preventivamente quel valore. (Rendi MaxValue statico per risolvere questo problema).

Esempio di tale chiamata.

int val = _getFromSomewhere(); 
var foo = new Foo(val); 
//This May produce compile time error 
// because the contract checker cannot prove you contract is met. 

La soluzione è quella di assicurarsi di inserire il vincolo in cui si effettua la chiamata.

int val = _getFromSomewhere(); 
if (val < Foo.MaxAmount) 
    var foo = new Foo(val); 
    //Will always compile fine, because contract is met. 

Quando si installa contratti, il controllo statico non è attivata per impostazione predefinita. Le proprietà del progetto avranno una scheda aggiuntiva in cui è possibile configurare le opzioni del contratto e abilitare il controllo statico.

+1

Si noti che l'uso di 'Contract.Requires' non è sufficiente. Hai ancora bisogno di un 'se (...) lanciare una nuova ArgumentOutOfRangeException()'. –

9

Potrei restituire un'eccezione invece di bloccare silenziosamente il valore, ma non è molto amichevole.

Cosa dire? Cosa intendi con "amichevole"? Il chiamante non è tuo amico, è un altro pezzo di codice che sta tentando di impostare un valore fuori dall'intervallo. Lo sviluppatore che ha scritto il codice dovrebbe essere informato immediatamente che sta facendo qualcosa di sbagliato.

Lanciare un'eccezione!

+1

Desidero * informare * il chiamante che il valore non è valido, ma senza eseguire il tentativo di istanziare la classe. Penso che sarebbe bello avere la risposta di Intellisense che non è valida, per esempio. –

1

O lanciare l'eccezione o fornire una proprietà statica per convalidare la quantità

public static bool ValidateAmount(int amount) 
{ 
    if(amount > MaxAmount) 
     return false; 
    return true; 
} 
2

Sarà necessario avvolgere il parametro in un nuovo tipo di allora. gli ints sanno qual è il loro importo massimo, ed è int.MaxValue. Se è vero che il parametro stesso conosce il proprio ammontare massimo, e che non è qualcosa di specifico per la classe Foo, allora sarà necessario creare un altro tipo che controlli l'importo passato a esso. Allo stato attuale, la firma del costruttore di Foo accetta qualsiasi struttura di dati int.

1

Non è detto che questo funzioni per i tipi di proprietà in C#, ma potresti essere in grado di definire un'enumerazione contenente tutti i valori accettabili e quindi impostare il tipo di dati della proprietà su tale enumerazione. Ciò costringerebbe il chiamante a utilizzare l'enumerazione e quindi a sapere quali valori sono accettabili. Naturalmente, se hai un sacco di valori nell'intervallo accettabile, l'enumerazione sarebbe ingombrante.

Problemi correlati