Release 1.2.20903 (4 Sep 2009) of Code Contracts comes with a nice integration of contracts on classes and methods into Sandcastle documentation! By this, Code Contracts achieve an important goal: real „checked documentation“. This short article gives an overview of how to get your Sandcastle installation to handle contracts.
1) Install Sandcastle (+DocProject)
At first, of course you need a valid Sandcastle installation. Just go to the Sandcastle project page, download the latest release and install it. I recommend installing DocProject for Sandcastle and HTML Help Workshop and Documentation as well. This gives you a nice Visual Studio integration for creating documentation by Sandcastle.
2) Patch Sandcastle with Code Contracts files
Code Contracts documentation syntax is currently not included in the Sandcastle project. In order to make Sandcastle aware of the Code Contracts documentation items, you have to patch the Sandcastle installation with the Code Contracts files. For this, there exists a folder „Sandcastle“ in the Code Contracts installation directory (default is „Program Files\Microsoft\Contracts\Sandcastle„), which contains a file „Sandcastle.zip„.
If you’ve installed Sandcastle from the MSI package, just copy the contents from the „msi\vs2005“ folder of the UIP file to the following location of your Sandcastle installation: „Program Files\Sandcastle\Presentation\vs2005\„. Now you’re done and ready to generate documentation for your contracts!
3) Set up your project
First, if you want to generate documentation for a project in Visual Studio, go to the project properties and in the „Build“ option pane, check the XML documentation file option:
Then, to get the contracts injected into the generated XML doc file, go to the „Code Contracts“ option pane and select both the Build a Contract Reference Assembly and the Emit contracts into XML doc file options:
Now, when you build your project, first the documentation of your project’s components will be created as XML file and then the contracts documentation will be injected in the same file.
4) Create your documentation!
For demonstation purposes, I’ve generated a project (including a new solution) AccountExample in Visual Studio, which holds just one class Account
. This example class contains some contracts and looks as follows:
/// <summary> /// Represents an account, to which you can deposit and from which you can withdraw money. /// </summary> public class Account { private float _balance; /// <summary> /// The current balance of the account. /// </summary> /// <value>The account's balance.</value> public float Balance { get { return _balance; } private set { Contract.Requires(value >= 0); _balance = value; } } [ContractInvariantMethod] protected void ClassInvariants() { Contract.Invariant(_balance >= 0); } /// <summary> /// Initializes a new instance of the <see cref="Account"/> class. /// </summary> /// <param name="balanceInitial">The initial balance, which will be set.</param> public Account(float balanceInitial) { Contract.Requires(balanceInitial >= 0); Contract.Ensures(Balance == balanceInitial); Balance = balanceInitial; } /// <summary> /// Deposits the specified amount to the account. /// </summary> /// <param name="amount">The amount to deposit.</param> public void Deposit(float amount) { Contract.Requires(amount > 0); Contract.Ensures(Balance == (Contract.OldValue(Balance) + amount)); Balance += amount; } /// <summary> /// Withdraws the specified amount from the account. /// </summary> /// <param name="amount">The amount to withdraw.</param> public void Withdraw(float amount) { Contract.Requires(amount > 0); Contract.Requires(amount <= Balance); Contract.Ensures(Balance == (Contract.OldValue(Balance) - amount)); Balance -= amount; } }
Moreover, I’ve added a new DocProject to my solution, where I’ve chosen the „AccountExample“ project for generating the documentation from. With that we’re ready to roll!
When building the whole solution, first the assembly of the „AccountExample“ project is built. In this step, the following XML documentation file is extracted as well (note the injected contract tags – requires
, ensures
and invariant
):
<?xml version="1.0"?> <doc> <assembly> <name>AccountExample</name> </assembly> <members> <member name="T:AccountExample.Account"> <summary> Represents an account, to which you can deposit and from which you can withdraw money. </summary> <invariant>_balance >= 0</invariant> </member> <member name="M:AccountExample.Account.#ctor(System.Single)"> <summary> Initializes a new instance of the <see cref="T:AccountExample.Account" /> class. </summary> <param name="balanceInitial">The initial balance, which will be set.</param> <requires>balanceInitial >= 0</requires> <ensures>Balance == balanceInitial</ensures> </member> <member name="M:AccountExample.Account.Deposit(System.Single)"> <summary> Deposits the specified amount to the account. </summary> <param name="amount">The amount to deposit.</param> <requires>amount > 0</requires> <ensures>Balance == (Contract.OldValue(Balance) + amount)</ensures> </member> <member name="M:AccountExample.Account.Withdraw(System.Single)"> <summary> Withdraws the specified amount from the account. </summary> <param name="amount">The amount to withdraw.</param> <requires>amount > 0</requires> <requires>amount <= Balance</requires> <ensures>Balance == (Contract.OldValue(Balance) - amount)</ensures> </member> <member name="P:AccountExample.Account.Balance"> <summary> The current balance of the account. </summary> <value>The account's balance.</value> <setter> <requires>value >= 0</requires> </setter> </member> </members> </doc>
In a second step of the build process, the DocProject generates the documentation for the „AccountExample“ project by taking its XML documentation file. When finished, we’ll have a nice CHM file with the documentation of our example project.
In this documentation we can see our contracts now. On class level, we have our invariant:
And on method (+properties) level, we get the pre- and postconditions:
Conclusion
As you can see, contracts in documentation are really valuable. Including them in Sandcastle documentation brings the claim of „checked documentation“ to life! This really helps other developers to understand the intent of your classes by looking at the documentation. And by automatically generating documentation out of your code-based contracts, you can be sure that docu and code do not run out of sync. This really puts more value on Code Contracts and is a real practical advantage!
Working with SHFB 1940 / Sandcastle 2700 the described patch seems not to work, it results in an error message during the Sandcastle build.
Any suggestion would be greatly welcome.