OdeToCode IC Logo

Thoughts on the Code Contracts Preview for .NET 4.0

Tuesday, February 24, 2009

A new Code Contracts preview is now available on DevLabs. Code Contracts will be part of the base class library in .NET 4.0 (included in mscorlib), and facilitate a Design by Contract programming approach. You can describe pre-conditions, post-conditions, and object invariants.

The Code

Let’s borrow a couple ideas from Matt’s DbC post (he was using Spec#, which was a precursor) to see what Code Contracts will look like.

public void Run()
{
    TargetResult result = LaunchMissle(new Target());
}

public TargetResult LaunchMissle(Target target)
{
    Contract.Requires(target != null);
    Contract.Ensures(Contract.Result<TargetResult>() != null);

    return new TargetResult();
}

In the LaunchMissle method we are using static methods on the System.Diagnostics.Contracts.Contract class to define a pre-condition (the target reference cannot be null), and a post-condition (the return value cannot be null). You can allow these contracts to verify not only the runtime behavior of your software, but you can also allow a static analysis tool to verify these contracts during builds. The following screen shot is from the new Code Contracts tab in the project options:

 

code contract options

Static analysis will find two problems when it analyzes the following code:

public void Run()
{
    TargetResult result = LaunchMissle(BuildTarget());
}

Target BuildTarget()
{
    return new Target();
}

public TargetResult LaunchMissle(Target target)
{
    Contract.Requires(target != null);
    Contract.Ensures(Contract.Result<TargetResult>() != null);

    return null;
}

The “return null” line in LaunchMissle is an obvious violation of the method’s contract, and the static analyzer will tell us so. However, it will also tell us that the pre-condition (target != null) is unproven. This is because the BuildTarget method doesn’t include a contract that guarantees it’s behavior. We could fix that with the following code:

Target BuildTarget()
{
    Contract.Ensures(Contract.Result<Target>() != null);
    return new Target();
}

This example demonstrates how enforcing a contract in one location can have a ripple effect on your code – which is something that becomes really painful if you’ve ever dealt with checked exceptions or generic constraints. Nevertheless, I’m still pretty optimistic about DbC in .NET. Built-in DbC constructs would have been in my top 3 list of “things to have in C#” 5 years ago. TDD has re-order my list dramatically, but I still feel DbC will still be a good addition and useful in some specific scenarios. 5 years ago I would have liberally applied contracts everywhere. Currently I’m thinking they’ll be best put to use on system boundaries.

MSIL Rewriting, TDD

One of the interesting features of Code Contracts is that it includes a MSIL rewriter (ccrewrite.exe) that post-processes an assembly to change the intermediate language instructions emitted by the compiler. I hope this elevates MSIL re-writing from a black art to a more mainstream technology that we can benefit from in the future. Re-writing could enable a number of cool scenarios in .NET, like AOP. I can’t help thinking that the Entity Framework team might have delivered POCOs in V1 if AOP was held in more regard.

Another great feature of Code Contracts is that you can turn static analysis on and off on a per project basis. I believe this will be important to anyone practicing TDD and BDD. You can see the issues in the comments of Matt’s post that I linked to earlier. Some people believe contracts eliminate the need for certain tests, and some people don’t.

I’ve done some thinking on this issue and I don’t want a contract to force a compiler error in my test.  For example, imagine a unit test that would pass null as the parameter to LaunchMissle. Static analysis can flag this as a problem because it violates the LaunchMissle contract. Should I delete the test? I vote no. Fortunately, it looks like I’ll be able to turn off static analysis when building my test project. TDD and BDD are a design process and I’ll write the test before I ever write the contract, and I feel that the eventual writing of the contract shouldn’t invalidate my unit tests. They have contracts and tests serve orthogonal purposes. As Colin Jack commented in Matt’s post:

What I'm saying is that yes the compiler will warn me and that I love, however I'd also want the option of being able to write a spec that ensures a particular contract is in place (if I do X I get Y). If I can't do that then refactoring of the code (not the specs) becomes less safe.


There are many more great features in the preview. Download the bits to check them out, or RTFM. Will Code Contracts be something you use in .NET 4.0?