BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Code Contracts in C#

Code Contracts in C#

Bookmarks

This article is taken from the book C# in Depth, Second Edition. The author explains different approaches to using Code Contracts. According to the author, to some developers contracts indicate the expected input states under which the method guarantees to operate correctly. To others, he says, contracts are a firmer guarantee, ensuring also that the code won’t execute at all if they fail. The author then describes how to get started with contracts. Lastly, the author discusses failure mode options and contract types based on the needs and context of code use, rounding up the article with a brief overview of validation.

Unless you happen to have used a language supporting Design by Contract before, you may sometimes find yourself unsure of how to proceed with Code Contracts. If you're using it in conjunction with Test Driven Development, what should you write first - the contract or the implementation? Should you write unit tests for the contracts? When is it appropriate to let ContractException be thrown, and when you should throw a standard .NET exception such as ArgumentNullException? Should your release builds check contracts or not?

We'll think about all of these questions in this article, but I don't promise to come up with concrete answers for everything. We’ll discuss the concepts and ideas of Code Contracts and the way they are handled in C#.

At the time of this writing, Code Contracts is a very young and evolving product. No-one has much experience with it, and I'm not going to claim to be an exception. Furthermore, many of these decisions are heavily dependent on your exact situation, and some are a matter of personal taste too. Hopefully with the discussion in this section you'll be in a good position to work out a strategy which is right for you and your team.

The Code Contracts user manual has guidance too, including situations where you don't want to run the binary rewriter at all. For the rest of this article I've assumed that you're happy to run the rewriter: it gives you a lot more control and options for no real disadvantages other than build time.

Philosophy: what's in a contract?

First, it's worth thinking a little about what contracts mean to you - in particular what preconditions mean. To some people, they indicate the expected input states under which the method guarantees to operate correctly. This is a little bit like having a warranty for a vacuum cleaner, which means the manufacturer will repair it if it breaks under normal conditions. They're not going to fix the cleaner if you start attacking it with a hammer though, and the company won't try to guarantee you won't be injured in such a situation either. In other words, if you break the terms of the warranty, all bets are off. People who regard contracts in this way are likely to have Debug.Assert calls within their existing code: they're measures to help you iron out any wrinkles in your code before you ship. However, if a bug creeps through and violates the preconditions in the released code, there's no telling what might happen. Likewise with postconditions, you've made a best effort to ensure that all the bugs have been fixed during development, but if a bug is still lurking you could end up with an invalid result. There probably won't be unit tests for failing preconditions, because there's no particular expected behavior.

To other people, contracts are a firmer guarantee: they not only say that the code will execute in a particular way if the preconditions are met, but also that the code won't execute at all if they fail. This is more like a height limit for a rollercoaster ride: the theme park isn't just willing to guarantee your safety if you're above a certain height, but they'll actively prevent you from putting yourself into danger if you fail to meet their criteria. Developers with this mentality are more likely to have explicit tests for bad argument values, throwing ArgumentNullException and so on, regardless of whether the code is built in debug or release mode. Postconditions have been harder to express until now, but this attitude is likely to encourage their enforcement in release builds too: a guarantee that the method really will never silently return a value that the contract forbids or leave the object in an invalid state. Preconditions are likely to have unit tests, as otherwise there's nothing to test the guarantees.

It's worth noting that these different groups may well agree on what the contracts for any particular operation should be: whether a particular value is a valid input, whether the method should guarantee the range of its return value, and so on. The difference is in what the behavior should be when that contract is violated. I'm not going to claim that either attitude is "wrong" although I tend to favour the latter approach. I generally dislike code which behaves significantly differently in a debug build to a release build. Whichever side you come down on, try to achieve consistency within a project - which for most developers will mean discussing the issue with other team members.

How do I get started?

Everyone has a different development style. Personally I like test-first coding, although I'm not as strict with myself as I'd like to be, and I couldn't really claim that all my design is test-driven. Before contracts, you already had the choice of whether to design the API first, the implementation first (changing the design as you went along), or the tests first. Now we've got to add contracts into the mixture. Fortunately, I don't think it's actually bad as it sounds - because I think each of those steps naturally yields its own set of contracts.

Sometimes tests will provoke contracts - usually preconditions. When you're writing "success case" unit tests, think about what else you could be passing into the method or other ways you could set up the object. Are there some combinations or arguments which don't make sense? Can you express them as contracts?

Sometimes the implementation will provoke contracts, both private ones (assertions and assumptions) and public ones (usually postconditions). It can be hard to know exactly what your code is willing to guarantee until you've written it, but if you can see something that will certainly be true when you exit, consider telling the caller about it as a postcondition.

Invariants are more likely to come out of the higher level design of the class, as they affect the whole type instead of specific members. One possibly obvious piece of advice: don't bother even thinking about invariants for immutable types. If something can't vary its state, it can't break an invariant. Instead, if there are rules which must be adhered to on construction, those should be preconditions.

Don't worry about capturing absolutely everything though. Just like with unit testing, it's better to have a few good contracts than decide that, because you haven't got enough time to put fully functional contracts in for everything, you won't bother at all. Likewise, it's worth thinking carefully about what you guarantee: while a weak postcondition is less useful for callers right now, it does give you more flexibility in the future. Try to think about the logical guarantees you want to make, even if you the implementation can guarantee a lot more.

Unit testing contracts

With legacy contracts, I tend to write unit tests to prove the contracts. Sometimes this feels like a waste of time - if you've got several parameters, none of which can be null, having a test for each of them can get extremely boring. However, it does prove that the behavior matches the documentation. With Code Contracts, you get that for free: if the autogenerated documentation includes a contract, it must be present in the code. That doesn't mean the contracts will be checked at execution time, of course - but unless you run your unit tests with every build configuration, a test couldn't check that anyway. I find it's particularly clean when you're implementing an interface, as then most of the contracts will be specified entirely separately to your implementation anyway, so there's less of a feeling of cheating.

It's entirely reasonable to write unit tests to provoke corner cases which you suspect may break the contracts, of course. That's just normal testing where you should test the expected result, and you'll get the added benefit of the contract performing sanity checking too.

If you decide you do want to test that your contracts are present and being checked, you may find a slight problem if you're using the default ContractException.

What is ContractException

ContractException is an exception type used by default for failed contracts that didn't have another one specified explicitly. The decision for whether to provoke an assertion failure or throw a ContractException is controlled by the "Assert on contract failure" option in the project properties. Note that it's an "either/or" option - if you trigger an assertion failure but then ignore it, the code will proceed normally – it doesn't throw a afterwards. is ContractException ContractException deliberately designed to be "uncatchable" - you can't refer to it in your own code. This is like the C# compiler generating "unspeakable names" for anonymous types and iterators.

The only way of catching a ContractException is to catch Exception. This is usually reserved for the top level of an application's stack, where you might want to prevent one bad request from bringing down a whole server - although you generally need to be careful of situations where you should terminate the whole process anyway. The rationale for this is that you never be catching contract should violations: they indicate bugs rather than some external failure, and it's hard to sensibly proceed in the face of "I'm calling code and something's gone wrong but I've no idea what."

So, ContractException is designed to be uncatchable at a fine-grained level, which makes it hard to use in traditional test framework "expect this action to throw this exception" assertions. I suspect that test frameworks will gradually evolve to include explicit support for this scenario, but, until then, one alternative is to use your own custom rewriter methods in a debug build. For instance, they could just throw a publicly available exception instead of the "hidden" ContractException, in which case you could expect that public exception just like any other failure. All of this is obviously unnecessary if you're using the "old-style" exceptions.

Before we get into configuring options, let's look at an example of how Code Contracts work in C#. We’ll show you how tools use Code Contracts through the example of a binary rewriter.

Code contracts in C#: Rewriting binaries with ccrewrite and ccrefgen

In the history of .NET, there have been many advances which have required two or more pieces of the ecosystem to both improve at the same time. Even features which don't require any explicit library or runtime support (such as object initializers and anonymous types) have typically been designed to fit into a grander plan. In the case of Code Contracts, the library and the supporting tools really are critical to each other. You express some contracts in code but then ignore the can tools. The preconditions will be checked (so long as you define the CONTRACTS_FULL or CONTRACTS_PRECONDITIONS preprocessor symbol); postconditions will generate an assertion telling you to use the rewriter; invariants simply won't be called. Likewise you use the binary rewriter without can expressing any contracts - but don't expect it to do anything useful.

We'll look at documentation and static checking later, but the rewriter is at the core of Code Contracts. Let's see what it can do.

Simple rewriting

Just as the name suggests, the binary rewriter takes the assembly you've just built, and rewrites parts of it. Usually it replaces the original assembly, but you can ask it to create a new one instead. Some of the operations are pretty much what you'd expect, but there are sophisticated features too. Let's start with the obvious ones.

For each method, the rewriter will rewrite it so that it follows this sequence of events:

  • Preconditions are checked
  • Initial state is captured for method calls OldValue
  • Functional part of code executes (including any assertions and assumptions being checked; they aren't moved around by the binary rewriter)
  • Postconditions are checked
  • Invariants are checked on public methods

Unless you have already specified a message in the contract, the rewriter examines your source code and rewrites the contract to use the actual code for the message. You have to build a pdb file for it to extract the source, but the default settings for Visual Studio work fine for both debug and release builds. The target of the contract call is also changed - instead of using the version of Contract in mscorlib, a new type is created in the rewritten assembly called __ContractsRuntime. This contains everything required at execution time, and also has ContractException.

Various options may remove some of these checks; for instance, you may only want to check preconditions for a release build, or only check contracts for public methods. You can use the "Perform runtime contract checking" and "Only public surface contracts" options in the project property page to control which contracts are included in the rewritten file.

The rewriter's behavior can be tweaked in a number of ways, and I'm not going to delve into every available switch, but I'll cover the most important options. If you can imagine some form of flexibility that might be useful, chances are ccrewrite supports it. This should reinforce the idea that contracts aren't "normal code." The C# compiler doesn't know anything special about contracts – it thinks they're normal code - but the rewriter can radically change how your code behaves.

If you look inside the rewritten code you'll see that there's slightly more to it even in the simplest case. Just in case you use a method or property with a contract as part of evaluating another contract, there's a recursion guard to stop the contract from blowing up the stack. Combining all of this together, our simplest contract method shown in listing 1 will be rewritten as listing 2.

Let’s look at listing 1 first, which shows how to signal a contract block end explicitly.

Listing 1 Signalling the end of a contract block explicitly

static int CountWhitespace(string text)
{
	if (text == null)
	{
		throw new ArgumentNullException("text");
	}
	Contract.EndContractBlock();
	return text.Count(char.IsWhiteSpace);
}

Now onto listing 2, showing binary rewriting.

Listing 2 A simple contract after binary rewriting

private static int CountWhitespace(string text)
{
	if (__ContractsRuntime.insideContractEvaluation <= 4)
	{
	     try
	     {
		__ContractsRuntime.insideContractEvaluation++;
		__ContractsRuntime.Requires(text != null, null, "text != null");
	     }
	     finally
	     {
		__ContractsRuntime.insideContractEvaluation--;
	     }
	}
	return text.Count(char.IsWhiteSpace);
}

Fortunately you'll rarely need to dive into the details of this, but it can make for some interesting reading - particularly when other options are involved. What appears to be a simple method can become really quite complex. Any concerns about the impact on performance should be measured rather than guessed, but it's worth considering that this extra complexity may well reduce the CLR's ability to inline code.

Another way to introduce complexity is to bring inheritance into the picture.

That's true in general, but Code Contracts brings an extra (very welcome) twist.

Contract inheritance

It's hard to say what I like best about Code Contracts, but contract inheritance is certainly one of the neatest features. It can be summed up in two rules:

When you override a method (or implement an interface method) you inherit its contracts.

You can't add extra preconditions to inherited ones, but you can make invariants and postconditions stronger.

Let's get the second rule out of the way first. It's really about Liskov's Substitution Principle: if a caller only knows that they're using an interface, and their call meets all the preconditions in the interface, it would be unfair to claim that they'd violated a contract which you happen to want on your implementation.

However, you guarantee that your implementation goes above and beyond the can call of duty when it comes to postconditions and invariants. Bearing that in mind, how does the first rule work? Well, essentially the rewriter inserts all the inherited preconditions and postconditions into the methods as it goes, and makes any invariant methods call the base invariant method if there is one. That's why the invariant method has to be at least protected for non-sealed classes. Listing 3 shows an example of this, as well as displaying the order in which the contracts are tested.

Listing 3 Contract inheritance with concrete classes

[Pure] static bool Report(string text)			Fake condition for diagnostics
{
	Console.WriteLine(text);
	return true;
}

class Base
{
	public virtual void VirtualMethod(string text)	Virtual method with contracts
	{
	     Contract.Requires(Report("Base precondition"));
	     Contract.Ensures(Report("Base postcondition"));
	}
}

class Derived : Base	
{
	public override void VirtualMethod(string text)	Overridden method with extra postcondition
	{
	     Contract.Ensures(Report("Derived postcondition"));
	}
}
...
Base d = new Derived();
d.VirtualMethod("");

The method is used to simulate a condition which is always Report satisfied, and also give an indication of what's going on without having to decompile the code. The code does nothing other than check the fake condition.

We have a virtual method declared in Base and overridden in Derived; both have postconditions but only the base declaration is allowed to specify preconditions. In the downloadable source code version, there are also invariants in both classes to demonstrate inheritance there, too. The output of listing 3 makes it clear that even though the override in Derived doesn't call base.VirtualMethod(), the contracts are still enforced:

Base precondition 
Derived postcondition 

Base postcondition

In the case of invariants, the base invariant is called before the derived one.

WARNING Breaking invariants sneakily

Invariants are only applied to methods... and the rewriter only public applies the invariant of the class that it knows about. If a public method in a base class calls a protected virtual method in a derived class, that method may break the derived class's invariant without it being checked. There's an example in the full source code, and there are other ways of observing an object while its invariants are broken unless you're careful.

The lesson is not to assume that invariants are bullet-proof. You should understand when they're expected to hold, and if you do expose the object in a "broken" state (such as via a callback) you should document that very clearly.

That's all very well and good, I hear you say - but what about abstract methods and interfaces? How can we express the contracts for methods we're not implementing? Good question. I'm personally somewhat wary of class inheritance – I think it's easy to abuse, and hard to design well. Interfaces, however, are great – it would be a real pity if we couldn't express contracts on them.

Fortunately, we can. We just need to create a class to implement the interface or derive from the abstract class, purely for the sake of expressing contracts. This is called the contract class. We then decorate the interface or abstract class with a [ContractClass(...)] attribute, and also add a [ContractClassFor(...)] attribute to the contract class. The contract class "implements" the methods just by a series of contract calls and the contracts are automatically applied to any implementations (assuming the appropriate rewriter options are turned on).

Interfaces and contract classes

When you implement an interface in a contract class, it has to use explicit interface implementation. This makes it a pain to call other methods in the interface to express the contract. For example, the contract class for IList<T> would probably want to express a precondition on the indexer requiring the index to be non-negative and less than Count. You can't just call Count directly, as it won't be visible through the implicit this reference due to the normal restrictions of explicit interface implementation.

To accommodate this, you can use a local variable of the interface type in the contract code, and assign it the value of this. You can then use that variable in the contract conditions. By convention, the local variable is called @this:

IList<T> @this = this; 
Contract.Requires(index < @this.Count); 

Listing 4 shows a very simple interface with a contract class and an implementation.

Listing 4 Specifying contracts for an interface

[ContractClass(typeof(ICaseConverterContracts))]	Specifies the contractclass
public interface ICaseConverter
{
	string Convert(string text);
}

[ContractClassFor(typeof(ICaseConverter))]		Declares what type contracts are for
internal sealed class ICaseConverterContracts : ICaseConverter
{
	string ICaseConverter.Convert(string text)
	{
	     Contract.Requires(text != null);		Applies pre and postconditions
	     Contract.Ensures(Contract.Result<string>() != null);
	     return default(string);			Returns a dummy value
	}

	private ICaseConverterContracts() {}		Prevents instantiation
}

public class CurrentCultureUpperCaseFormatter : ICaseConverter
{
	public string Convert(string text)
	{
	     return text.ToUpper(CultureInfo.CurrentCulture);	Inherits contracts from interface
	}
}

This is basically the simplest interface I could imagine with useful pre and postconditions. It's meant to convert a string to a particular case (lower, upper, title etc). You should never pass in a null reference, and implementations should never return a null reference. The interface specifies that its contracts are in ICaseConverterContracts, and that refers back to the interface. The suggested convention is to name the contract class after the interface, just with a Contracts suffix. This looks slightly odd as there's then a class with an I-prefixed name, but it's effectively just representing metadata about the interface.

It's not a normal class in terms of implementing real behavior. (The CLR considers it to be an entirely normal class, of course - it's only the Code Contracts tools and us as developers that make the distinction.) You may wish to consider keeping the contracts class in the same file as the interface, again with the justification that it's really just metadata and not a separate concept.[1]

In the "contracts-only" implementation of Convert, we express the precondition and postcondition in the normal way before returning a default value. You could explicitly return null of course - I just find that using default(...) gives more of a hint that this isn't meant to be treated as a "real" result. Of course this would actually violate the postcondition if we actually executed it, but that doesn't matter as this code is only present for the sake of the contracts. To make sure that we never really create an instance of the contract class, I've given it a private constructor which is never called from the class itself. I've also made it internal so that it won't confuse developers in other projects.

Finally we've implemented the interface in a normal way in CurrentCultureUpperCaseFormatter. The implementation doesn't specify any contracts or do any checks itself. Instead it relies on the binary rewriter to enforce the contracts at execution time.

Now admittedly the contract is separated from the interface itself, but apart from that this is a really situation to be in. We don't have to test that the great interface implementations honor the preconditions, and although we may write tests to try to provoke the postconditions being violated in corner cases, we can be confident that such violations won't silently be propagated to calling code (assuming the appropriate rewriter settings, of course). Callers can also be certain of what they're allowed to do - the preconditions are clearly stated, and implementations can't have added their own ones. Of course there's nothing to stop an implementation from checking and throwing an ArgumentException anyway, but in a system based on Code Contracts this is a very conscious violation of best practices, rather than the accidental introduction of an extra requirement.

Contract reference assemblies

Code Contracts has more capabilities up its virtual sleeves. Sometimes you may not want the contracts to be present in the executable code, but you want to let callers use the contracts anyway. Another tool (ccrefgen) lets you build a contract reference assembly to distribute along with your normal assembly. The contract reference assembly contains all the types, interfaces, methods and so on of the original assembly, but with no implementation except the original contracts. It will have the same name as the original assembly, but with .Contracts at the end. For example, an assembly of SkeetySoft.Media would have a contract reference assembly called SkeetySoft.Media.Contracts. When another developer adds a reference to your main assembly, the Code Contract tools will look for the contract reference assembly to go with it. If it finds the contracts, it can use those for static checking and also enforce the preconditions in the assembly. For example, consider the code in listing 5, which is caller's split between a simple library class and a call to it:

Listing 5 Adding preconditions at the call site

public class PreconditionDemo				In a class library
{
	public void DontPassInNull(string text)
	{
	     Contract.Requires(text != null);
	     Console.WriteLine("In DontPassInNull()");
	}
}
...							In a separate assembly
new PreconditionDemo().DontPassInNull("hello");

Suppose the class library is built without execution time contract checking turned on, but with a separate contract reference assembly. The calling assembly can tick "Call-site requires checking" in the Code Contacts project property page, at which point the last line is converted into something like this:

System.Diagnostics.Contracts.Wrappers.ContractAssemblyDemo.
 
PreconditionDemo.NV$DontPassInNull(new PreconditionDemo(), "hello");

The extra type here has been built into the calling assembly; its NV$DontPassInNull method executes the preconditions copied from the contract reference assembly (in the normal way, with calls to __ContractsRuntime.Requires) and then calls the real DontPassInNull method if the preconditions pass. It's all seamless to the caller - they get the same behavior as if preconditions were turned on in the target assembly.

WARNING Only preconditions are copied

Using "Call-site requires checking" doesn't enforce postconditions or invariants. In some cases it wouldn't be able to - they can refer to the internal state of an object, which may not be visible to the caller. That shouldn't be a problem though - you do trust the code you're calling to be perfect, don't you?

Of course if you turn this feature on and the target assembly also has execution time checking, you'll end up testing the preconditions twice, which isn't ideal.

Failure behavior

So far we've looked carefully at how to express the contracts and whether or not they're checked but hardly considered what happens when they're violated. The behavior on failure is relatively complicated and highly tweakable, but it usually boils down to three possible options:

  • A normal exception type will be thrown if you've specified one in a precondition
  • The system may raise an "assertion error" window or break into the debugger if you're already debugging
  • A ContractException (created by the binary rewriter) will be thrown otherwise

All of this talk of exceptions and assertions is assuming you haven't included any custom error handling of your own. There is a global event raised whenever a contract fails: Contract.ContractFailed. This allows subscribers to observe the failure and optionally state that it has been handled. A handled failure won't trigger an exception or breaking into the debugger. Listing 6 shows an example of this sort of masking as well as showing what information is available in the event.[2]

Listing 6 Masking a contract with Contract.ContractFailed

static void RequireNonNullArgument(string text)		Method with precondition to violate
{
	Contract.Requires(text != null, "Don't pass in null");
	Console.WriteLine("In method body");
}

static void HandleFailure(object sender,		Failure handling method
		ContractFailedEventArgs args)
{
	Console.WriteLine("{0}: {1}; {2}", args.FailureKind,
	     args.Condition, args.Message);
	args.Handled = true;
}
...
Contract.ContractFailed += HandleFailure;		Subscribes to global failure event
RequireNonNullArgument(null);				Triggers failure

Contract.ContractFailed += HandleFailure; Subscribes to global failure event

RequireNonNullArgument(null); Triggers failure

We start off with a method with a precondition. This could be any contract which is easily violated, but we're just checking for argument non-nullity. Nothing new here. If the precondition is satisfied, we print out a diagnostic message.

HandleFailure is the method we're going to use to create an EventHandler<ContractFailedEventArgs> delegate for the event. First, it writes out the kind of failure (precondition, ContractFailed postcondition, invariant, assertion or assumption), the condition which failed (in this case "text != null") and the message (if any - in this case "Don't pass in null").

It then claims that we've handled the failure. If there are multiple handlers, they'll still all get called, but a later handler can't reset the failure to be "unhandled". If the failure ends up being handled, that's the end of it - control is returned to the caller as if the contract had been satisfied.

To test the method we first attach the event handler and then call the method in a way which will violate the precondition. Despite the failure, we still get into the method body. Here's the output:

Precodition: text != null; Don't pass in null 
In method body 

Another way of influencing what happens on contract failure is to use custom rewriter methods. In the project properties page, you can specify an assembly and class to use. Any of the static methods present in that class which match the signatures used by the contracts runtime will be used instead of the default behavior. For example, if you want to change how assertions are handled, you would write a method like this:

public static void Assert(bool cond, string userMsg, string condText) 
{ 
	Console.WriteLine("Checking condition {0}", condText); 
	Console.WriteLine("Result of check? {0}", cond); 
}

Any required methods which aren't available in the class are given the usual behavior. See the documentation provided with the Code Contracts download for the other method signatures.

Changing the failure behavior either for a whole assembly (using custom methods) or a whole AppDomain (using the ContractFailed event) isn't something you'll want to do for normal applications. It's mostly useful if you're writing a custom framework which will execute third party code: a test harness or a plugin system, for example. It's worth knowing about what can happen mostly so that if you're executing in an environment that does change the failure mode, you won't be surprised. I expect most mainstream unit test frameworks to gain support for Code Contracts in the near future. Most of the time you should be thinking about any differences in behavior you want to see between a debug build and a release build. The binary rewriter's job is to change how the code behaves at execution time.

Now that we’ve seen an example of how tools use contracts, let's think about how you might want to configure Code Contracts in the first place.

Options, options everywhere

You certainly can't fault Code Contracts when it comes to flexibility. Combining the various features of the tools with different build configurations, you can achieve almost any effect you want. The difficult part isn't the tweaking - it's deciding what you want to start with.

Choosing a failure mode

Unless you start creating your own custom rewriter methods or subscribing to the event, you have four mutually exclusive ContractFailed options for what should happen if a contract is violated:

  • The application stops with an assertion failure, breaking into the debugger if one is attached.
  • A ContractException is thrown, which can't be caught except as a blanket Exception.
  • A standard .NET exception is thrown, which can be caught by client code.
  • Nothing - the code continues as if no contracts were involved.

Some of this can be decided on a per-configuration basis - in particular, whether to use ContractException or assertions, and how many contracts to check - whereas the decision to use standard .NET exceptions require the types to be specified in the code itself. While you could write every precondition twice, using preprocessor symbols to determine which to use for a given configuration, it sounds like a step too far to me.

Unless you are migrating an existing project with legacy contracts to use Code Contracts, I would personally recommend using the non-generic Requires method which will either assert or throw a ContractException. It results in the simplest code, and you don't have to repeat the parameter name for ArgumentException-related preconditions. The familiarity of existing exceptions tugs at me a little, but fundamentally the type of the exception doesn't provide any extra information beyond what is already in the contract – and removing the possibility of catching specific exceptions is a step towards encouraging appropriate exception handling policies. One disadvantage of ContractException is that as you can't explicitly catch it; it's hard to write unit tests to check your contracts, as we've just seen.

If you build a contract reference assembly, you can let your callers have some control over what happens too. There's little point in building a reference assembly if the contracts are present in the rewritten binary anyway though. Having said that, a reference assembly will only help with certain contracts...

Contract types

Different contract types can have different behavior associated with them. There isn't complete freedom about what to do here - you can't easily make some contracts fail with a ContractException and others fail with an assertion error, for example - but the binary rewriter does let you specify the general level of contracts to keep in the rewritten binary:

  • None: all contracts, including legacy ones, will be removed
  • Release requires: legacy contracts and preconditions which specify a standard .NET exception will be included
  • Preconditions: as above, but with preconditions throwing ContractException included as well
  • Pre and post: as above, but with Contract.Ensures (and Contract.EnsuresOnThrow) calls included
  • Everything: the above, with invariants, assertions and assumptions included

If you really want to include some of the later items but exclude earlier ones, you could use C# preprocessor directives to achieve another degree of flexibility. For example, you may want full contract checking in an ideal world but have some postconditions which are just too expensive to validate in release builds. You could use a preprocessor symbol of EXPENSIVE_CONTRACTS and only define it in debug builds - or even have a separate build configuration. That leads us to consider what kinds of build you want in the first place.

Different choices for different projects

This is a good example of where different projects may well have different requirements. The BCL team are scrupulous in unit testing their contracts, and their binaries ship with a relatively small set of contracts included at execution time. They're in a fairly unique position - they have relatively little idea of how the code is going to be used, so any performance impact from contracts could be very serious indeed.

They also don't have the luxury of letting users decide which version to run against - we really don't want to start having different configurations of .NET installed on end-user machines for different contract preferences. Your own requirements will no doubt be different - so think about them carefully.

Additionally, there's the question of where the contracts are applied. If your code has a lot of contracts for debugging and verification purposes but at execution time you mostly care about the behavior at the boundaries between your code and third-party assemblies, you can turn on the "Only public surface contracts" option.

Build configurations

Visual Studio creates projects with Debug and Release configurations by default. The general idea is that you develop using the Debug configuration but then ship using Release. But what counts as "shipping" anyway? Code is used in an increasingly diverse way - shipping a shrink-wrapped consumer application is very different to deploying your code to the one-and-only server running your production web service, which is also very different to releasing a new version of an open source library. Try to work out who will want to use the code and in what context. If you're building a class library, do you know the other developers and their needs? Will some developers be concerned about the performance of checking contracts frequently? Are they likely to know about Code Contracts, and perhaps want to use the static checker against your library's contracts (even if perhaps you don't use the checker yourself)?

If you're building an application so the code won't be consumed be other developers, only users, that makes life easier: you can decide the behavior you want to give you confidence that the application won't malfunction, and tune the settings accordingly. Open source projects are easy in the other direction: at least then, if you don't get your users' needs correctly, they can change the settings and rebuild the code themselves. The hardest situation is perhaps that of the component vendor, shipping class libraries as binaries to the paying customers.

You can introduce more flexibility in what you ship by creating multiple build configurations - some with full checking, others with only public API checking of preconditions, and so on. Beware of the cost of maintaining a large number of configurations though - especially if there are other variables involved other than contracts. You probably don't want to get into the business of building multiple versions to target different versions of the .NET framework different and levels of contracts and different optimisation levels and so on.

Assertion failures are reasonable for debug builds (whether you're the one doing the debugging or not) but are almost certainly not right for anything which might reasonably be called a release build. This can be decided on a per-configuration basis easily from the options. I know this goes against my earlier point of debug and release builds behaving differently, but at least in both cases execution of that unit of work will halt - it's just the difference between bubbling up a "nearly uncatchable" exception to a top level handler and raising an ugly but attention-grabbing assertion window.

Why not validate everything?

I would generally suggest writing defensive code which validates both its inputs and its own behavior. There's one obvious potential drawback: performance. Checking a contract is clearly "more work" than not checking it. If you stick to contracts which are cheap to validate, they're unlikely to become a performance bottleneck other than for small methods called a huge number of times - but if you have any performance benchmarks, it would be worth running them both with and without execution time contract checking so you can tell for sure. As ever, it's hard to make accurate guesses about performance - so don't. Gather data which is as realistic as possible, and base your decision on that.

If you've got internal methods which are called very frequently, you may find that just restricting contracts to the public API is good enough. If you do turn execution time contract checking off, and if you're building a class library, it makes sense to build a contract reference assembly. There are no downsides to this, other than the build taking very slightly longer, and it at least gives others the option of running call site validation or static checking against your code.

Summary and prospects

I have high hopes for Code Contracts. I suspect it will take a while to gain traction, partly as it requires an additional download even before you get started, and partly because some of its goodness is currently only available to Visual Studio Ultimate users. However, I believe it addresses some of the issues which have frustrated developers for a long time.

It's worth acknowledging that part of the problem is that the type system in .NET isn't quite rich enough to start with. If non-nullable reference types were embedded in .NET and C#, a whole class of preconditions and postconditions would be unnecessary now. Likewise the fact that .NET doesn't have any notion similar to in C++ is a source of pain whenever you const want to return a read-only "view" of an object to a caller, or declare that you won't mutate the state of any incoming objects. Code Contracts doesn't address this latter issue yet, but I wouldn't be surprised to hear that engineers were thinking about it quite hard.

Contracts become more helpful the more widely used they are, particularly in terms of static checking. There's no guarantee that Code Contracts will become an industry-wide expectation - that in two years' time you'd be considered mad to ship a commercial component library without a contracts reference assembly. Microsoft is effectively bootstrapping the ecosystem by adding contracts to the BCL, and we can hope that leading library vendors (both free and commercial) will adopt them over time. Until then, you can still benefit from them within your own code, in terms of simple execution time checking, autogenerated documentation, automatic contract inheritance, possibly static analysis, and above all the encouragement to just stop and think about what you need and what you're prepared to guarantee.

At a deep level, all code is about communicating: expressing ideas about what you want to achieve. It's no coincidence that if you look at the features C# has gained over the years, almost all of them have been around communicating with common and useful ideas in a simple manner. It's one of the ways our industry is attempting to manage the increasing complexity we're all faced with. Code Contracts is another weapon in the armory. Don't just think of it as a way of making sure that your code isn't faced with inappropriate values at execution time. Think of it as a means of letting your code speak with clarity and precision - whether that's to the tools, the code maintainer, other developers using your API, or even yourself.


[1] If C# allowed interfaces to declare nested types, this would be an ideal situation to use that ability. Unfortunately, it doesn't.

[2] This doesn't work with the VS2010b1 version of Code Contracts. It's a little bit behind the curve. The next beta of VS2010 should make it all work, at which point I'll update the text where necessary. In particular, this example compiles but doesn't do what you'd expect at execution time. In the next version, we'll need to call SetHandled instead of using the Handled property. I also need to take note of SetUnwind(), but the behavior isn't clear enough for me to speculate on yet.


InfoQ readers can get 35% off any version of "C# in Depth", 2nd Edition (print book+early access or Ebook+early access) with the checkout code "infoq35". The offer is exclusively available at www.manning.com, and expires May 1, 2010.

Rate this Article

Adoption
Style

BT