Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Cutting Edge:

Impostazioni dei contratti codice in Visual Studio 2010

Dino Esposito

image: Dino EspositoIl mese scorso ho introdotto contratti software come implementazione di Microsoft.NET Framework 4. Noto come codice appalti, contratti software consentono di indicare le condizioni formali che del codice deve soddisfare per il corretto funzionamento. I contratti di codice che il codice generare un'eccezione se un metodo non può ricevere i dati come previsto o se produce dati successivi non postconditions previsto. Per una panoramica delle precondizioni e postcondizioni, si potrebbe desiderare di controllare il mio articolo nel numero del mese scorso (msdn.microsoft.com/magazine/gg983479).

I contratti di codice fanno parte di al.NET Framework 4, ma si basano inoltre su alcuni impianti nel 2010 di Visual Studio, ad esempio gli strumenti della fase di esecuzione, integrazione con MSBuild e una pagina delle proprietà della finestra delle proprietà del progetto. È importante notare che è sufficiente scrivere le precondizioni e postconditions non è sufficiente. È inoltre necessario attivare la funzionalità per ciascun progetto per divertirsi con i contratti di software di controllo di runtime. In tal caso tramite la pagina delle proprietà progetto codice contratti nel 2010 di Visual Studio.

In questo articolo verrà illustrato lo scopo delle varie opzioni di verifica o selezionare ed esaminare le strumento rewriter e procedure consigliate per l'operazione più comune che fare con contratti di codice: convalida dell'argomento.

Pagina delle proprietà dei contratti di codice

Deve imposta codice contratto precondizioni e postconditions in tutte le generazioni o solo in debug build? In pratica si tratta verso il basso la concezione di un contratto software. È parte dello sforzo di progettazione? Oppure si tratta di una misura di debug?

Se si tratta di una funzione di progettazione, non è necessario eliminare le contratti nelle build di rilascio. Se si tratta di una tecnica di debug, quindi non si desidera fare in modo che intorno quando si esegue la compilazione in modalità di rilascio.

Nel.NET Framework, codice contratti sono una parte del framework e non sono baked in qualsiasi lingua. Di conseguenza, è più semplice per configurarli in base all'interno del progetto per ogni generazione. I.Implementazione di NET Framework di contratti software lascia pertanto all'utente decidere dove e quando è opportuno implementare i contratti.

Figura 1 mostra la pagina delle proprietà in Visual Studio 2010, attraverso il quale si configurano come volete contratti software di lavorare per un'applicazione. Si noti che tali impostazioni si applicano sulla base del progetto e possono essere adattate se del caso.

The Property Page for Code Contracts in Visual Studio 2010

Figura 1 la pagina delle proprietà per il codice dei contratti in Visual Studio 2010

È possibile selezionare la configurazione ideale (Debug, rilascio e così via) e applicare le impostazioni solo per tale configurazione. In questo modo, è possibile attivare i contratti di codice per il debug, ma non per il rilascio e, ancora più importante, è possibile annullare la decisione in qualsiasi momento.

Controllo della fase di esecuzione

Per attivare i contratti di codice è necessario selezionare l'opzione eseguire verifica contratto Runtime. Se si lasciano che non è selezionata, quindi eventuali istruzioni di contratto che è possibile racchiudere il codice sorgente non produrrà alcun effetto (ad eccezione di Contract.Assert e Contract.Assume in una generazione in cui è definito il simbolo DEBUG, ma che è sorta di un punto secondario). La casella di controllo Controlla se alla fine di ogni passaggio di compilazione viene attivato lo strumento rewriter. Il rewriter è uno strumento esterno post-processes software contratti e modifica il codice MSIL, immissione di verifica preliminare, postcondition e invariante nella posizione corretta.

Errore di asserzione nota, tuttavia, se si dispone di una precondizione come questo, si ha intenzione di ottenere un runtime se calpestare il rewriter:

Contract.Requires<TException>(condition)

Figura 2 mostra la finestra di messaggio che si ottiene.

The Code Requires Runtime Contract Checking

Figura 2 il codice richiede contratto Runtime verifica

Per vedere come il controllo runtime in dettaglio, si consideri il seguente codice:

public Int32 Sum(Int32 x, Int32 y) {
  // Check input values
  ValidateOperands(x, y);
  ValidateResult();

  // Perform the operation
  if (x == y)
    return 2 * x; 
  return x + y;
}

I dettagli del contratto vengono archiviati nei metodi ValidateXxx utilizzando ContractAbbreviators come discusso nell'articolo del mese scorso. Ecco il codice sorgente per i metodi di ValidateXxx:

[ContractAbbreviator]
private void ValidateOperands(Int32 x, Int32 y) {
  Contract.Requires(x >= 0 && y >= 0);
}

[ContractAbbreviator]
private void ValidateResult() {
  Contract.Ensures(Contract.Result<Int32>() >= 0);
}

Se si utilizza Contract.Requires invece di Contract.Requires <TException>, allora si salva te stesso dal fallimento della Figura 2 se si mantiene il masterizzatore in una delle compilazioni. Il messaggio scatola Figura 2 è dovuto l'implementazione interna di Contract.Requires, che assomiglia a questo:

[Conditional("CONTRACTS_FULL")]
public static void Requires(bool condition, string userMessage) {
  AssertMustUseRewriter(
    ContractFailureKind.Precondition, "Requires");
}

public static void Requires<TException>(bool condition) 
  where TException: Exception {
  AssertMustUseRewriter(
    ContractFailureKind.Precondition, "Requires<TException>");
}

L'attributo Conditional indica ai compilatori che una chiamata di metodo deve essere ignorata a meno che non è stato definito il simbolo CONTRACTS_FULL. Questo simbolo è definito solo quando si attiva l'opzione eseguire verifica contratto Runtime. Poiché Contract.Requires <TException> non è definito in modo condizionale e non dispone dell'attributo, il controllo rewriter viene eseguito e genera un'asserzione non riuscita se il controllo di runtime contratto è disabilitato.

È possibile spostare in avanti e considerare l'effetto dell'utilizzo di rewriter nel codice precedente. Si può verificare per se stessi il sto parere solo utilizzando i punti di interruzione e premere Ctrl + F11 per visualizzare la visualizzazione Disassembly nel 2010 di Visual Studio. Figura 3 mostra il contenuto della vista Disassembly quando fate un passo attraverso il metodo Sum compilato senza abilitazione del controllo di runtime contratto. Come si può vedere, il codice sorgente è la stessa come viene scritto nella classe.

Disassembly View Without Runtime Contract Checking

Figura 3 vista smontaggio senza contratto di Runtime verifica

Quando si attiva il controllo della fase di esecuzione, lo strumento rewriter passa attraverso restituiti dal compilatore e consente di modificare il codice MSIL. Se fate un passo attraverso lo stesso codice con contratti di codice abilitato, si vede qualcosa di simile Figura 4.

Postconditions Checked Past the Return Statement

Figura 4 postcondizioni controllato passato l'istruzione Return

La differenza più importante è che ValidateResult viene richiamato a destra prima di chiudere il metodo Sum e oltre l'istruzione return. Non è necessario essere un guru di MSIL per leggere quello che sta succedendo nel codice in Figura 4. Gli operandi sono convalidati prima il metodo avvia rispettando la posizione più in alto di condizioni preliminari. Il codice che contiene postconditions viene spostato verso il basso del metodo e il codice MSIL per l'istruzione return finale appena rientra in esso. Più interessante la prima istruzione return, ovvero quello che nel metodo somma implementa un collegamento, ovvero ora appena passa l'indirizzo in cui inizia il ValidateResult:

...
restituire 2 * x;
eax mov 00000054, dword ptr [ebp-8]
00000057 aggiungere eax, eax
00000059 mov dword ptr [ebp-0Ch], eax
nop 0000005c
0000005d jmp 0000006B
...
ValidateResult();
0000006b push dword ptr ds: [02C32098h]
...

Ritorno al Figura 1, notare l'elenco a discesa vicino la casella di controllo eseguire controllo Runtime contratto. Tale elenco consente di indicare il numero di contratti di software che si desidera attivare. There are various levels: Full, Pre and Post, Preconditions, ReleaseRequires and None.

Full significa che sono supportati tutti i tipi di contratti di software e nessuno indica che nessuno di essi sono presi in considerazione. Pre e Post sono esclusi gli invarianti. Condizioni preliminari sono escluse anche le istruzioni di verifica.

ReleaseRequires non supporta il metodo Contract.Requires e solo consente di specificare le condizioni preliminari utilizzando richiede <TException> o il formato If-Then-Throw legacy.

La pagina delle proprietà di progetto consente di attivare o disattivare il controllo sulla base del progetto, ma cosa succede se si desidera disattivare il runtime verifica solo in alcune sezioni del codice runtime? In tal caso è possibile semplicemente disattivare il controllo runtime a livello di programmazione utilizzando l'attributo ContractRuntimeIgnored. Tuttavia, una versione più recente (1.4.40307.0) aggiunta una nuova opzione Ignora quantificatori che consente di non eseguire tutti i contratti che contengono riferimenti a Contract.ForAll o Contract.Exists.

È possibile applicare l'attributo ai membri utilizzare nelle espressioni di contratto. Se il membro è decorato con l'attributo dell'intera istruzione di contratto in cui è inserita non viene sottoposto a controllo in fase di esecuzione. L'attributo non è riconosciuto in metodi quali Assert e Presupponi del contratto.

Modalità di assemblaggio

Le proprietà di contratti di codice consentono di configurare l'impostazione di modalità di assemblaggio per i contratti. L'impostazione si riferisce a come si desidera eseguire la convalida dell'argomento. There are two possible options: Standard Contract Requires and Contract Reference Assembly. Le impostazioni della modalità Assemblaggio utili strumenti quali rewriter per ottimizzare il codice e fornire gli avvisi appropriati quando necessario. Si supponga che si utilizza la modalità di assemblaggio per dichiarare l'utilizzo previsto del codice contratti per la convalida dei parametri. Le impostazioni della modalità Assemblaggio forniscono un paio di semplici regole, che è necessario soddisfare, in caso contrario si otterrà un errore di compilazione.

Modalità assieme deve essere impostato su Standard contratto richiede se si utilizzano metodi richiede e <T> per convalidare gli argomenti del metodo. È consigliabile utilizzare la convalida dei parametri personalizzati se si utilizza un'istruzione If-Then-Throw come condizioni preliminari. Se non si utilizza la convalida dei parametri personalizzati, l'istruzione verrà trattata come un richiede <T>. La combinazione di convalida dei parametri personalizzati e utilizzare in modo esplicito di qualsiasi forma di richiede istruzioni, invece, genererà un errore di compilazione.

Qual è la differenza tra l'utilizzo è necessario e utilizzando le istruzioni If-Then-Throw? Sempre un'istruzione If-Then-Throw genera l'eccezione che indica se la convalida ha esito negativo. A tale riguardo, si differenzia dal richiede, ma è simile a richiede <T>. Un'istruzione If-Then-Throw normale inoltre non è rilevabile dagli strumenti di contratto (rewriter e controllo statico) a meno che non è seguita da una chiamata a EndContractBlock. Quando si utilizza EndContractBlock, deve essere l'ultimo metodo codice contratto che si richiama il metodo. Nessuna chiamata di codice contratti mai può essere seguito:

if (y == 0)
  throw new ArgumentException();
Contract.EndContractBlock();

Inoltre, richiede che le istruzioni vengono ereditate automaticamente. Se non si utilizza EndContractBlock, un'istruzione If-Then-Throw non è ereditata. In modalità legacy, If-Then-Throw contratti non vengono ereditati. È invece necessario eseguire manualmente l'ereditarietà del contratto. Gli strumenti cercherà di avvisare se non rilevano che le condizioni preliminari vengono ripetute in override e implementazioni dell'interfaccia.

Si noti infine che ContractAbbreviators non possono contenere tutte le istruzioni If-Then-Throw, ma è possibile utilizzare controlli di convalida di contratto per cui. Abbreviators può includere solo istruzioni contratto regolare per la convalida dell'argomento.

Further impostazioni

Nella pagina delle proprietà codice contratti, se si seleziona l'opzione eseguire verifica contratto Runtime verrà attivare alcune utili opzioni aggiuntive.

Se si attiva l'asserzione in caso di errori di contratto, tutte le violazioni del contratto comporta un'asserzione che descrive il contesto dell'errore. Vedrete una finestra di messaggio simile a ciò che viene mostrato Figura 2 e verranno fornite alcune opzioni da scegliere. Ad esempio, possibile, tentare nuovamente a collegare un debugger, interrompere l'applicazione o ignorare l'errore e continuare.

È possibile utilizzare questa opzione per le build di debug solo perché le informazioni visualizzate non potrebbe essere significativi per l'utente medio. L'API di contratti di codice offre un gestore delle eccezioni centralizzata che consente di acquisire qualsiasi violazione, lasciando all'utente di capire cosa esattamente non ha funzionato. Le informazioni ricevute consente di distinguere se una condizione preliminare, una postcondition o invariante non riuscita, ma viene utilizzata solo l'espressione booleana ed eventualmente il messaggio di errore configurate per caratterizzare l'errore. In altre parole, è un po' difficile recuperare normalmente dal gestore eccezioni centralizzata:

Contract.ContractFailed += CentralizedErrorHandler;

Ecco di seguito viene illustrato il gestore del codice:

static void CentralizedErrorHandler(
  Object sender, ContractFailedEventArgs e) {
  Console.WriteLine("{0}: {1}; {2}", e.
FailureKind, e.Condition, e.Message);
  e.SetHandled();
}

Se si desidera generare eccezioni specifiche in fase di esecuzione, quindi l'utilizzo richiede <TException> è il modo di procedere. È possibile utilizzare richiede e il gestore centralizzato se si desidera limitare l'uso di contratti per il debug di generazioni o se non è rilevante che cos'è il tipo effettivo dell'eccezione. È abbastanza spesso per indicare che si è verificato un errore. Nella parte superiore, ad esempio, molte applicazioni hanno un catch-all che intercetta tutti i tipi di eccezione e provvederà automaticamente al riavvio.

The Only Public Surface Contract option refers to where you would like to have Code Contracts enforced: every method or only public methods. Se si seleziona l'opzione, il rewriter Ignora le istruzioni di codice contratto sui membri privati e protetti ed elabora solo i contratti sui membri pubblici.

Selezionare questa opzione è utile se si incorporano codice contratti nell'ambito del progetto generale e, di conseguenza, utilizzarli in tutto il mondo. Tuttavia, una volta pronto per la consegna dell'applicazione, come una forma di ottimizzazione è possibile disattivare il carico aggiuntivo di controllare i parametri sui membri interni perché nessun codice esterno mai da richiamare tali membri direttamente.

Se la limitazione dei contratti di codice alla superficie pubblica di un assembly è una buona idea dipende anche dal modo in cui scritto il codice. È un modulo di ottimizzazione è in grado di garantire che le chiamate effettuate nell'area pubblica a livelli inferiori siano corretti. In caso contrario, la disattivazione di contratti per i metodi interni è possibile trasformare nell'origine dei bug pericolosi.

L'opzione sito di chiamata richiede il controllo viene utilizzato un altro scenario di ottimizzazione. Si supponga che si sta scrivendo una libreria per essere utilizzato dai moduli in altri assembly. Per motivi di prestazioni, si disattiva il controllo dei contratti di runtime. Tuttavia, fino a quando è inoltre possibile creare un assembly di riferimento del contratto, consente al chiamante di controllare il contratto per ogni metodo chiamato.

Potrebbe esistere un assieme di riferimento del contratto per ogni assembly che contiene classi che utilizzano i contratti di codice. Contiene l'interfaccia dell'assembly padre con le istruzioni del contratto, ma nessun altro codice visibile pubblicamente. La creazione dell'assembly può essere ordinata e controllata dalla pagina delle proprietà di contratti di codice.

Qualsiasi codice che intende richiamare la raccolta può fare riferimento all'assembly di riferimento del contratto e Impossibile importare automaticamente contratti di attivando semplicemente l'opzione è necessario controllare il sito di chiamata. Durante l'elaborazione del codice chiamante, il rewriter consente di importare il contratto per ogni metodo chiamato su un assembly esterno è dotato di un assieme di riferimento del contratto. In questo caso, il contratto viene archiviato nel sito della chiamata, ovvero sul lato chiamante, ovvero rimane un comportamento facoltativo che può essere attivato e disattivata per il codice è non controllare direttamente.

Conclusioni

I contratti di codice è un'area di al.NET Framework che vale la pena molto ulteriori indagini. Mi hai accenno delle opzioni di configurazione qui e non hanno ancora effettuato all'utilizzo del correttore statico. I contratti di codice consentono di progettare le applicazioni in modo più chiaro e scrivere codice più chiaro.

Per ulteriori informazioni su contratti di codice, vedere il giugno 2009 CLR Inside Out colonna da Melitta Andersen (msdn.microsoft.com/magazine/ee236408) e il sito DevLabs codice contratti (msdn.microsoft.com/devlabs/dd491992). Troverete anche informazioni interessanti circa lo sviluppo del codice contratti presso il sito di Microsoft Research codice contratti (research.microsoft.com/projects/contracts).

Dino Esposito è l'autore di "Programming Microsoft ASP.NET MVC"(Microsoft Press, 2010) e coautore di"Microsoft.NET: Architettura delle applicazioni per l'impresa" (Microsoft Press, 2008). Vive in Italia e partecipa spesso come relatore a eventi del settore in tutto il mondo. Seguirlo su Twitter a twitter.com/despos.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Mike Barnett