CodeContract.Requires( you.PayAttention() );
E’ ovvio che rivoluzioni come Linq si vedono una volta nella vita quindi secondo me non ha senso lamentarsi dell’apparente veste poco rivoluzionaria del nuovo framework, se però scaviamo a fondo, sotto sotto ci sono tante belle cose…
Una delle cose che mi attraggono e che verranno incluse nel nuovo runtime sono i Code Contracts, che sono già “utilizzabili” ora anche con VS2008, utilizzabili tra virgolette perchè l’attuale licenza permette solo sperimentazioni e nulla di più.
Vediamo di approfondire un po’ la questione:
class OrderProcessor { public void Process( Order order ) { } }ipotizziamo una situazione elementare come quella esposta, quello di cui dobbiamo preoccuparci è l’inevitabile e tedioso controllo della validità dei parametri in ingresso, classicamente quello che possiamo fare è:
class OrderProcessor { public void Process( Order order ) { if( order == null ) { throw new ArgumentNullException(); } } }se usiamo un pizzico di AOP possiamo scrivere una cosa decisamente più smart:
class OrderProcessor { public void Process( [NotNull]Order order ) { } }e otteniamo lo stesso identico comportamento. Ma non è tutto oro quel che luccica, purtroppo ;-), e quello che ci manca, e che a me piacerebbe molto è la possibilità di avere questo controllo direttamente a compile time, del resto quella non è logica di business ma è un constraint “punto”.
Vediamo cosa possiamo fare con i “Code Contracts”, una volta scaricato e installato il tutto quando creiamo un nuovo progetto di Visual Studio ci troviamo qualcosa in più, nella pagina delle proprietà c’è infatti una nuova tab:
se quindi aggiungiamo una reference all’assembly “Microsoft.Contracts” e riproviamo a compilare il codice del primissimo esempio, quello senza nessun controllo sui parametri, succede questo:
toghissimo!, Visual Studio al termine della compilazione invoca un nuovo tool (cccheck.exe) che fa analisi statica del codice e stabilisce che li c’è un potenziale problema. Già questo, a mio modo di vedere sarebbe, o meglio sarà, una vera manna dal cielo.
Ma non è finita, per eliminare il warning è sufficiente fare in modo che il codice sia safe quindi ad esempio rimettere il check sui parametri:
class OrderProcessor { public void Process( Order order ) { if( order == null ) { throw new ArgumentNullException(); } } }a questo punto Visual Studio compila felice come una pasqua, ma non è detto che questo ci basti, se questo è una condizione sufficiente a proteggerci a runtime rimane sempre la fastidiosa situazione che il tutto avviene a runtime mentre se avvenisse a compile time saremmo molto più felici.
Come fare in modo che Visual Studio si lamenti se trova una situazione come questa:
OrderProcessor op = new OrderProcessor(); op.Process( null );Con Code Contracts è decisamente semplice:
public void Process( Order order ) { CodeContract.Requires( null != order ); }aggiungendo questa dichiarazione e ricompilando quello che otteniamo è:
dove il primo warning con un doppio click ci porta al punto nel codice in cui c’è la condizione, mentre il secondo ci porta al punto in cui la condizione viene violata. La cosa fighissima è che funziona anche se il codice che “recupera” l’istanza di Order è decisamente complesso.
Faccio notare che Visual Studio genera degli Warning e, secondo me, è giusto così del resto il codice dal punto di vista del compilatore è corretto l’IDE ci informa solo dei potenziali problemi a cui potremmo andare incontro.
In questo esempio, quindi, per far si che il controllo statico a compile time passi senza warning è sufficiente soddisfare il contratto:
class Program { static void Main( string[] args ) { Order o = Compless.GetOrder(); if( o != null ) { OrderProcessor op = new OrderProcessor(); op.Process( o ); } } }E’ quindi il chiamante che deve assicurarsi di effettuare i controlli di rito. Avendo soddisfatto il contratto succede un’altra cosa molto interessante, se spulciamo con il fido Reflector il codice generato scopriamo che non vi è traccia alcuna:
Questo porta all’indubbio vantaggio che le performance ne giovano perchè i controlli sulla coerenza dei parametri in ingresso vengono fatti solo ed esclusivamente dove hanno senso, notiamo infatti che un’eventuale chiamata del tipo:
op.Process( new Order( 100 ) );viene perfettamente digerita.
Ci sono però delle casistiche in cui potremmo volere che il controllo venga effettuato anche a runtime, un “classico” esempio potrebbe essere quello della Class Library, noi scriviamo la libreria e altri la usano, tutto ciò è possibilissimo, basta cambiare la dichiarazione:
public void Process( Order order ) { CodeContract.RequiresAlways( null != order ); }e il fido Reflector conferma:
Garantendoci un’exception a runtime se il contratto non fosse rispettato.
Ma andiamo oltre, è si non è finita :-D…
Supponiamo di trovarci nella casistica appena esposta, il nostro ruolo è quello di developer di framework e non conosciamo chi saranno i nostri utilizzatori ma non vogliamo neanche il potenziale impoverimento delle perfromance che il controllo a runtime potrebbe comportare: si può fare
Condizione necessaria è abilitare sul/i progetto/i class library la generazione di un assembly per i contratti, quello che succede a questo punto quando compiliamo è che vengono generati 2 assembly/progetto: uno con la class library vera e prorpia e uno con i soli contratti. Sempre dal fido Reflector scopriamo che se il codice che compiliamo è questo:
public void Process( Order order ) { CodeContract.Requires( null != order ); Int32 qty = order.ItemQty; }il prodotto sarà nella class library:
mentre nell’assembly dei contratti:
sulla macchina dell’utilizzatore il motore di analisi statica del codice sarà nuovamente in grado di fare tutti i suoi ragionamenti e verificare il codice che accede alla class library notificando i potenziali problemi, garantendo anche all’utilizzatore che il contratto sia stao rispettato.
Quello che abbiamo visto è solo una piccola parte di quello che si può fare, le varie “assert” possibili con Code Contracts sono veramente tante, la documentazione fortunatamente, anche se ridotta ad un semplice pdf, è esaustiva.
Una cosa di cui non ho parlato, ma che è decisamente potente, è che c’è anche la possibilità di esprimere contratti sulle interfacce, eccezziuuunale veramente
Attenzione: è una cosa in divenire… e come tale va presa con le pinze. Quello che sappiamo è che:
- ci sarà nella RTM del fx 4.0;
- i nomi cambieranno: CodeContract –> Contract;
molto probabilmentenon sarà più un assembly Microsoft.Contracts e quindi non sarà più necessario aggiungere la reference a mano, con il CLR v4.0 il namespace System.Diagnostics.Contract sarà definito in mscorlib.dll;
Che dire a me piace un sacco… CodeContract.Requires( ( Mauro as IHappyDeveloper ).BeginUsing() );
.m