Introduction
I found the article about code contracts in this month's MSDN very exciting. I was unaware of this feature of .Net 4.0 before reading the article, and as the manager of a team of developers who maintain a very large, complex application I am always interested in techniques that improve code quality and correctness. I won't rehash the interesting details about code contracts in this article. Go read the MSDN article first or this Code Project introduction. The basics are all there. Then go here to the Microsoft DevLabs page with the download you'll need to run the code below.
My first thought when reading the MSDN article was that the benefits must come at some price, and my initial concern was an impact on performance. Contracts are enforced at run-time by inserting custom code at compile time, and whenever some other process is adding code to mine I worry about hidden performance costs.
I wrote the small app below to get some metrics and assess how expensive contracts are compared to the other techniques that can be used to validate pre and post execution conditions.
Using the code
The author of the MSDN article used a trivial calculator function to highlight the benefits of contracts. I'll use the same basic function here:
private static Int32 Add(Int32 x, Int32 y) {
if (x == y)
return x * 2;
return x + y;
}
The extra if statement in there is just to reinforce the difficulty of adding post-condition checking everywhere in your code where you have premature exits. I'm going to leave it here for the analysis.
"If-Then-Throw"
One way to check pre and post conditions is to use explicit if statements to validate your input parameters and output results. All of the pre and post conditions we're adding here are perfectly arbitrary, but will naturally be consistent in all the examples.
private static Int32 IfCheckedAdd(Int32 x, Int32 y) {
if (x < 0) // Arbitrary pre-condition
throw new InvalidOperationException("X must be greater than 0");
if (x == y) {
if (x * 2 < 0) // Arbitrary post-condition
throw new InvalidOperationException("Result must be positive");
return x * 2;
}
if (x + y < 0) // Same arbitrary post-condition
throw new InvalidOperationException("Result must be positive");
return x + y;
}
Debug.Assert
Another way is using Debug.Assert()
private static Int32 AssertCheckedAdd(Int32 x, Int32 y) {
System.Diagnostics.Debug.Assert(x >= 0);
if (x == y) {
System.Diagnostics.Debug.Assert(x * 2 >= 0);
return x * 2;
}
System.Diagnostics.Debug.Assert(x + y >= 0);
return x + y;
}
Contracts
And the last method to examine is the interesting new one, code contracts:
private static Int32 ContractCheckedAdd(Int32 x, Int32 y) {
Contract.Requires(x >= 0, "X must be greater than 0");
Contract.Ensures(Contract.Result<Int32>() >= 0, "Result must be positive");
if (x == y)
return x * 2;
return x + y;
}
One of the benefits to contracts being that you don't have to worry about where you exit. All of your post checking conditions are centralized at the top of the method.
Read more: Codeproject