Introduzione ai Code Contracts (parte 5)

Scritto da  Massimo Bonanni il mercoledì 29 settembre 2010
Linguaggio: VB   •  Framework: 3.5,4.0   •  Livello: 200

Download sorgenti


Speciale Code Contracts

Questo articolo è la quinta ed ultima puntata (prima, seconda, terza e quarta) della serie dedicata ai Code Contracts.

In questa puntata vedremo come sia possibile generare automaticamente la documentazione delle classi che riporti precondizioni, postcondizioni e invarianti di oggetto.

Riprendiamo la classe Corso costruita negli articoli precedenti:

Imports System.Diagnostics.Contracts
''' <summary>
''' Corso
''' </summary>
''' <remarks></remarks>
Public Class Corso

''' <summary>
''' Numero massimo alunni ammessi al corso.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property NumeroMassimoAlunni As Integer

''' <summary>
''' Elenco degli alunni iscritti.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property Alunni As List(Of Alunno)

''' <summary>
''' Costruttore.
''' </summary>
''' <param name="numeroMassimoAlunni">Numero massimo di alunni ammessi al corso.</param>
''' <remarks></remarks>
Public Sub New(ByVal numeroMassimoAlunni As Integer)
Contract.Requires(numeroMassimoAlunni > 0, "Il numero massimo degli alunni deve essere positivo")
Me.NumeroMassimoAlunni = numeroMassimoAlunni
Me.Alunni = New List(Of Alunno)
End Sub

''' <summary>
''' Permette di iscrivere un alunno al corso.
''' </summary>
''' <param name="alunno">Alunno da iscrivere al corso.</param>
''' <returns>Ritorna la posizione dell'alunno nella lista degli iscritti.</returns>
''' <remarks></remarks>
Public Overridable Function IscriviAlunno(ByVal alunno As Alunno) As Integer
Contract.Requires(alunno IsNot Nothing, "L'alunno non può essere Nothing")
Contract.Requires(NumeroMassimoAlunni > Alunni.Count, "Massimo numero di alunni raggiunti")
Contract.Ensures(Me.Alunni.Count = Contract.OldValue(Me.Alunni.Count) + 1)
Contract.Ensures(Contract.Result(Of Integer)() > 0)

Alunni.Add(alunno)
Dim retval = Alunni.Count
Return retval
End Function

<ContractInvariantMethod()> _
Private Sub ObjectInvariant()
Contract.Invariant(Me.Alunni IsNot Nothing)
Contract.Invariant(Me.NumeroMassimoAlunni > 0)
End Sub

End Class

 

Cominciamo con il generare il normale file XML che contiene la documentazione "standard".
Per fare questo è sufficiente attivare il checkbox Generate XML documentation File nel tab Compile delle proprietà di progetto (come mostrato nella seguente figura):

Fig1

Se compiliamo il nostro progetto, nella cartella Bin\Debug dovremmo trovare il file CodeContractConsole.xml il cui estratto riguardante la classe Corso è il seguente:

<member name="P:CodeContractsConsole.Corso.NumeroMassimoAlunni">
<summary>
Numero massimo alunni ammessi al corso.
</summary>
<value></value>
<returns></returns>
<remarks></remarks>
</member>
<member name="P:CodeContractsConsole.Corso.Alunni">
<summary>
Elenco degli alunni iscritti.
</summary>
<value></value>
<returns></returns>
<remarks></remarks>
</member>
<member name="M:CodeContractsConsole.Corso.#ctor(System.Int32)">
<summary>
Costruttore.
</summary>
<param name="numeroMassimoAlunni">Numero massimo di alunni ammessi al corso.</param>
<remarks></remarks>
</member>
<member name="M:CodeContractsConsole.Corso.IscriviAlunno(CodeContractsConsole.Alunno)">
<summary>
Permette di iscrivere un alunno al corso.
</summary>
<param name="alunno">Alunno da iscrivere al corso.</param>
<returns>Ritorna la posizione dell'alunno nella lista degli iscritti.</returns>
<remarks></remarks>
</member>
<member name="T:CodeContractsConsole.Corso">
<summary>
Corso
</summary>
<remarks></remarks>
</member>

 

Si tratta, cioè, del classico file di documentazione cui siamo abituati.
Per inserire automaticamente, all'interno di questo file, la documentazione riguardante i contratti definiti nella classe Corso, si debbono eseguire alcuni passi.
Innanzitutto è necessario attivare la modifica del file XML precedente da parte del tool dei Code Contracts deputato alla generazione della documentazione (ccdocgen.exe) e per fare questo apriamo il tab Code Contracts delle proprietà di progetto e modifichiamo le impostazioni come mostrato in figura:

Fig2

Nel momento in cui compiliamo, il tools di generazione della documentazione dei Code Contracts dovrebbe modificare il nome del file XML originale aggiungendo l'estensione .old e creare un nuovo file XML a partire dall'originale con, all'interno, i nuovi tag per la documentazione dei contratti.

Se eseguiamo la compilazione su un progetto C# (ad esempio quello contenuto nei sorgenti allegati), troveremo, al termine della stessa, entrambi i file all'interno della cartella bin\Debug.

La versione 1.4.30707.2 dei tools su cui sono state eseguite le demo di questi articoli, non si comporta esattamente allo stesso modo per i progetti VB.NET.
A causa di bug che stato corretto nella versione 1.4.30903.0, infatti, il generatore di documentazione crea correttamente entrambi i file ma li lascia all'interno della cartella dei file temporanei di compilazione. Per maggiori informazioni potete consultare il seguente link.

Se abbiamo installato la nuova versione, la nostra documentazione è pronta nella cartella degli eseguibili per essere utilizzata con strumenti di generazione dei file di help quali, ad esempio, SandCastle.

Se, invece, stiamo utilizzando la versione dei tools di luglio 2010 e apriamo la cartella obj\x86\Debug (che potrebbe variare in base a configurazione e piattaforma impostate sul progetto) troviamo file CodeContractConsole.xml  e il file CodeContractConsole.xml.old. Il primo dei due file contiene la documentazione completa dei contratti da noi inseriti per la classe Corso:

<member name="P:CodeContractsConsole.Corso.NumeroMassimoAlunni">
<summary>
Numero massimo alunni ammessi al corso.
</summary>
<value />
<returns />
<remarks />
</member>
<member name="P:CodeContractsConsole.Corso.Alunni">
<summary>
Elenco degli alunni iscritti.
</summary>
<value />
<returns />
<remarks />
</member>
<member name="M:CodeContractsConsole.Corso.#ctor(System.Int32)">
<summary>
Costruttore.
</summary>
<param name="numeroMassimoAlunni">Numero massimo di alunni ammessi al corso.</param>
<remarks />
<requires description="Il numero massimo degli alunni deve essere positivo">numeroMassimoAlunni &gt; 0</requires>
</member>
<member name="M:CodeContractsConsole.Corso.IscriviAlunno(CodeContractsConsole.Alunno)">
<summary>
Permette di iscrivere un alunno al corso.
</summary>
<param name="alunno">Alunno da iscrivere al corso.</param>
<returns>Ritorna la posizione dell'alunno nella lista degli iscritti.</returns>
<remarks />
<requires description="L'alunno non può essere Nothing">alunno IsNot Nothing</requires>
<requires description="Massimo numero di alunni raggiunti">NumeroMassimoAlunni &gt; Alunni.Count</requires>
<ensures>Me.Alunni.Count = Contract.OldValue(Me.Alunni.Count) + 1</ensures>
<ensures>Contract.Result(Of Integer)() &gt; 0</ensures>
</member>
<member name="T:CodeContractsConsole.Corso">
<summary>
Corso
</summary>
<remarks />
<invariant>Me.Alunni IsNot Nothing</invariant>
<invariant>Me.NumeroMassimoAlunni &gt; 0</invariant>
</member>

 

Sono stati aggiunti, quindi, i tag XML <invariant>, <requires> e <ensures> che rappresentano rispettivamente le invarianti di oggetto, le precodizioni e le postcondizioni. L'attributo description conterrà l'eventuale messaggio da noi impostato e, in ogni caso, è riportata la condizione del contratto in formato testo.

Una volta aggiunti i tag di documentazione all'XML, possiamo, come già detto in precedenza, utilizzare SandCastle  per generare l'help vero e proprio. Nella cartella di installazione dei tools dei Code Contracts è presente un folder in cui è dettagliatamente spiegato come modificare i file di SandCastle per interpretare i nuovi tag di documentazione.

Per terminare vediamo com'è possibile aggirare il bug del generatore di documentazione e fare in modo che, a seguito della compilazione, entrambi i file di documentazione si trovino nella cartella bin\Debug come per la versione C#.

Per fare questo utilizzeremo uno script di post-build. Visual Studio permette di eseguire degli script prima e dopo una compilazione in modo da intervenire preventivamente o a posteriori sui file di progetto. Quello che faremo è farein modo che, a seguito di una compilazione avvenuta con successo, venga eseguita una copia dei due file di cui sopra (se esistono) all'interno della cartella di bin attuale.

Iniziamo con il creare il semplice script PostBuildDoc.bat: 

if exist %1.xml xcopy /C /Y %1.xml %2
if exist %1.xml.old xcopy /C /Y %1.xml.old %2

 

e salviamolo nella cartella dove è presente la soluzione contenente il nostro progetto.

Attiviamo lo script di post build  utilizzando il comando Build Events.. che troviamo nel tab Compile delle proprietà di progetto:

Fig3

La pressione del tasto permette di visualizzare la finestra degli eventi di compilazione:

Fig4

Nello spazio relativo ai post-build event inseriamo la seguente linea di comando:

call "$(SolutionDir)PostBuildDoc.bat"
"$(ProjectDir)obj\$(PlatformName)\$(ConfigurationName)\$(TargetName)"
"$(ProjectDir)$(OutDir)"

 

nella quale richiamiamo lo script batch precedentemente creato.

A questo punto il gioco è fatto e, a seguito di una compilazione eseguita correttamente, troveremo i file di documentazione all'interno della cartella degli eseguibili.

Riferimenti

 


Tags: Code Analysis,Code Contracts,SandCastle

 
x