March 2009 - Posts

.NET 4.0 feature als preview voor .NET 3.5: Code Contracts (Dutch)
Fri, Mar 6 2009 2:19 PM

Op de PDC heeft Microsoft een aantal nieuwe features bekend gemaakt die in .NET 4.0 zullen komen. Een van deze features is Code Contracts. Van Code Contracts heeft Microsoft een preview versie voor Visual Studio 2008 Team edition uitgebracht. Nu kan je in je huidige omgeving Code Contracts uitproberen. Code Contracts voorzien in Design by Contract.

Wat is Design by Contract

Design by Contract (DBC) is door Bertrand Meyer in de taal Eiffel voor het eerste gebruikt. Hij heeft het mogelijk gemaakt om contracten vast te leggen tussen aanroepende (client) en aangeroepen code (supplier).

De vragen die continue afgevraagd kunnen worden bij de aanroep van code:

  • Wat verwacht het? De preconditie: wat moet waar zijn voordat de aanroep gedaan wordt
  • Wat garandeerd het? De postconditie: wat is waar na afloop van de aanroep
  • Wat wordt er altijd gegarandeerd? De Invariant: wat is continue waar.

De client dient bij de aanroep naar de supplier de precondities te controleren. De supplier garandeerd na het controleren van de precondities dat het resultaat voldoet aan de postcondities. Daarnaast zorgt de supplier dat de invariant condities worden nageleefd.

Om deze reden is er sprake van een contract tussen client en supplier. Indien je meer wil weten van DBC kan je hier eens kijken: http://en.wikipedia.org/wiki/Design_by_contract

Dat kan toch ook met Asserts

Design by Contract is een manier om defensief te programmeren. Met Asserts kan je in C# soortgelijke eisen vastleggen. Echter met code contracts is meer mogelijk. Code Contracts zijn ‘Asserts on steroids’. De meerwaarde die Code Contracts bieden zijn:

Zichtbaarheid van Precondition: Asserts zijn niet zichtbaar voor aanroepende code. Met Code Contracts zijn preconditions wel zichtbaar. Niet alleen voor ontwikkelaars die de methode aanroepen, maar ook voor tooling.

Postconditions: Asserts gebruiken voor postcondities is niet eenvoudig. Zal snel leiden tot duplicate code en is niet erg onderhoudbaar. De postcondities met Code Contract worden net als de Precondities bovenaan de methode gedeclareerd. Met IL-rewrite word door de tooling op alle juiste exit plaatsen van de method de postconditie geïnjecteerd.

Inheritance: Overriden methods dienen dezelfde precondities en postcondities te hebben als hun parent methods. Met Asserts zou je in zowel de parent als overriden method dezelfde Asserts hebben staan. Dit zorgt voor duplicate code. Het is niet mogelijk om deze condities consistent te houden. Code contracts zorgen wel voor consistency en inheritence van contracts.

Microsoft Research project: Spec#

Microsoft Research heeft Design by Contract allereerst vastgelegd in Spec#. Met dit project heeft Microsoft onderzocht wat Design by Contract voor .NET kan betekenen. Code Contracts zijn het resultaat van dit research project.

Code Contracts preview

De preview kan gedownload worden vanaf DevLabs (een portal waar meerdere projecten van Microsoft opstaan waarvan de bits te downloaden zijn en die dus uitgeprobeerd kunnen worden) http://msdn.microsoft.com/en-us/dd491992.aspx

De losse documentatie kan hier gedownload worden

http://download.microsoft.com/download/C/2/7/C2715F76-F56C-4D37-9231-EF8076B7EC13/userdoc.pdf

Na installatie heb je een nieuwe directory: %Program Files%\Microsoft\Contracts.

Hierin bevinden zich de tools die de static analysis mogelijk maken. Assemblies om naar te refereren en Code Contracts te gebruiken. Code Snippets om sneller condities te programmeren en Samples.

In .NET 4.0 zal in mscorlib de class Contract bevinden onder namespace System.Diagnostics.Contracts

Om Code Contracts in .NET 3.5 te ondersteunen is de assembly Microsoft.Contracts.dll in het leven geroepen. De class Contract bevind zich ook in de namespace System.Diagnostics.Contracts

Wat biedt Code Contracts

Code Contracts biedt een manier om aannames in code vast te leggen in de vorm van Precondities, Postcondities en Invariants. Deze contracten worden gebruikt om het volgende mogelijk te maken:

  1. Verbeteren van Unit Testen via runtime checks
  2. Statische contract controle
  3. Documentatie generatie

Er worden 2 tools meegeleverd

  1. Runtime checking. Een binary rewriter die contracten injecteerd in een programma. (ccrewrite.exe)
  2. Static checking. Deze statische controle tool kan op basis van de contracten zien of er schendingen zijn zonder dat het programma wordt uitgevoerd. (cccheck.exe)

Voorbeeld

In deze introductie post allereerst een simpel voorbeeld om de functionaliteit te laten zien van de pre, post en invariant condities.

    1 public class RekeningList : List<Rekening>

    2 {

    3     public Rekening GetByName(string userName)

    4     {

    5         Contract.Requires(!string.IsNullOrEmpty(userName));

    6         Contract.Ensures(Contract.Result<Rekening>() != null);

    7 

    8         return this.Where(r => r.Name == userName).FirstOrDefault();

    9     }

   10 }

   11 

   12 public class Rekening

   13 {

   14     public string Name { get; set; }

   15 

   16     public void Stort(double bedrag)

   17     {

   18         Contract.Requires(bedrag > 0);

   19 

   20         saldo += bedrag;

   21     }

   22 

   23     public void NeemOp(double bedrag)

   24     {

   25         Contract.Requires(bedrag > 0);

   26         Contract.Requires(saldo >= bedrag);

   27 

   28         saldo -= bedrag;

   29     }

   30 

   31     private double saldo = 0;

   32 

   33     public double Saldo

   34     {

   35         get { return this.saldo; }

   36     }

   37 

   38     [ContractInvariantMethod]

   39     protected void CorrectSaldo()

   40     {

   41         Contract.Invariant(saldo >= 0);

   42     }

   43 }

In bovenstaand voorbeeld staat op regel 5 een precondition. Deze conditie zou hier ook nog met een Assert opgelost kunnen worden. Op regel 6 staat een Postcondition. Je zou verwachten dat een postcondition op het einde van de method gezet zou moeten worden. Als deze assembly gecompileerd wordt, wordt er op de juiste plaats(en) code geinjecteerd die voor deze controle zorgen. De syntax van deze Postcondition zorgt ervoor dat er geen tijdelijke variabele gemaakt dient te worden van het type Rekening die uiteindelijk geretourneerd gaat worden.

Voor de geinteresseerden: met reflector ziet de GetByName methode er als volgt uit:

public User GetByName(string userName)
{
    __copy+<>c__DisplayClass1 CS$<>8__locals2 = 
new __copy+<>c__DisplayClass1(); CS$<>8__locals2.userName = userName; __ContractsRuntime.Requires(!string.IsNullOrEmpty(
CS$<>8__locals2.userName), null, "!string.IsNullOrEmpty(userName)"); <>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1(); CS$<>8__locals2.userName = userName; User CS$1$0000 = this.Where<User>(new Func<User, bool>(
CS$<>8__locals2.<GetByName>b__0)).FirstOrDefault<User>(); User Contract.Result<User>() = CS$1$0000; __ContractsRuntime.Ensures(Contract.Result<User>() != null,
null, "Contract.Result<User>() != null"); return Contract.Result<User>(); }

Vanaf regel 38 staat een voorbeeld van de Invariant condition. Deze condition houdt continue in de gaten of het saldo van de rekening nog wel gelijk of groter is dan 0. Hierdoor is het niet nodig om deze aanname op tig plaatsen te coderen, maar staat deze code op 1 plaats.

Indien het runtime checken van de Code Contracts niet aangezet wordt zal er niets met deze code gedaan worden. Bij de properties van een project is er een nieuwe tab bijgekomen na intallatie van Code Contracts. Bij Runtime Checking dient de checkbox aangevinkt te worden.

CodeContractTab

De tweede tool is static checking. Ook dit kan aangezet worden op een project. Bij het compileren krijg je dan meldingen als bij de aanroep van een method waarop niet gecontroleerd wordt of de parameter Rekening null is:

contracts: Possibly calling a method on a null reference 'rekening'

Visual Studio Team Edition ondersteuning

Op dit moment heeft Microsoft als doel om Code Contracts alleen voor de Team Editions beschikbaar te maken (en de academic versie). Op deze manier wordt het research project terugbetaald. Echter net zoals Unit Testen is het ontwikkelen met Design By Contract ook wenselijk op goedkopere versies van Visual Studio. Op het forum vraagt het Code Contracts team hierover response. Als je het team wil laten weten dat je ook op andere versies van Visual Studio Code Contracts ondersteuning wil kan dat hier: http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/459bdcbe-8af2-4781-b1fa-eec1c9d79a52

Toekomst

Code contracts brengen mogelijkheden voor andere tools. Bijvoorbeeld voor unit testen. Hiervoor is ook via DevLabs, PEX te downloaden. http://msdn.microsoft.com/en-us/devlabs/cc950525.aspx. PEX is een tool die een unit test suite genereert op basis van onder andere code contracts waardoor de code een zeer hoge codecoverage heeft.

Samenvattend

Code Contracts is een feature die in het .NET Framework 4.0 officieel tot onze beschikking komt. Microsoft heeft een preview gereleased waarmee op het .NET 3.5 framwork al met deze functionaliteit gespeeld kan worden. Code Contracts bieden een manier om aannames in code vast te leggen op verschillende manieren. Dit wordt ook wel Design By Contract genoemd. Code contracts biedt zowel runtime als static checking van condities.