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?


Comments
Rob White Tuesday, February 24, 2009
I like the ideas we have here, but why are we doing this as code and not as attributes to the code?

It just strikes me the the contract is not really part of the logic of the code, but rather a detail to the code?

Maybe it talks about this in the pdf file you link to, but each time I download it it comes up as corrupt.
Matt Freeman Tuesday, February 24, 2009
This seem like a glorified version of debug.assert, when trying a project out via TDD this kind of seems redundant, perhaps I'm missing something?

Hopefully some clear guidelines will emerge though, the last thing we need is another software house that follow idesign guidelines to the tee - replace assert abuse with dbc abuse.
Paul Batum Tuesday, February 24, 2009
Scott, whats your take on the fact that the latest code contracts release only supports the Team System edition of VS?
scott Tuesday, February 24, 2009
@Matt - similar, but the static analysis is powerful. For example - generating docs from the code.

@Paul - I'm actually trying to get some clarification on that. Will post here when I do...
scott Tuesday, February 24, 2009
@Rob - It isn't mentioned in the PDF, but the BCL team says: "The problem with this approach is that attributes are very limited in what they can express. It would be difficult or impossible to use attributes for all of the contracts you can write in code." blogs.msdn.com/...
James Brechtel Tuesday, February 24, 2009
Will these contracts be supported on interfaces? Since they're in method bodies it doesn't seem like they will be. But it seems like a huge opportunity is lost if they're not supported on interfaces.

If nothing else, I'd love to be able to specify which arguments can't be null for methods on an interface.
scott Tuesday, February 24, 2009
@James: Yes, they can. You can decorate the interface with an attribute that points to a class with contracts inside method and property pseudo-implementations. Those contracts apply to anyone implementing the interface. It's an interesting approach.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!