I don't know where the assertion that "it does not and often cannot perform a static analysis" comes from. The first part of the assertion is plainly wrong. The second one depends of what you mean by "often". I would rather say that often, it performs static analysis, and rarely, it fails at it. In ordinary business application, rarely becomes much closer to never.
So here it comes, the first benefit:
Benefit 1: static analysis
Ordinary assertions and argument checking have a drawback: they are postponed until the code is executed. On the other hand, code contracts manifest themselves at a much earlier level, either at the coding step or while compiling the application. The earlier you catch an error, the less expensive it is to fix it.
Benefit 2: sort of always up to date documentation
Code contracts also provide a sort of documentation which is always up to date. If the XML comment of the method SetProductPrice(int newPrice)
tells that newPrice
should be superior or equal to zero, you may hope that the documentation is up to date, but you may also discover that somebody changed the method so that newPrice = 0
throws an ArgumentOutOfRangeException
, but never changed the corresponding documentation. Given the correlation between code contracts and the code itself, you don't have the out-of-sync documentation problem.
The sort of documentation provided by code contracts is also precious in a way that often, XML comments don't explain the acceptable values well. How many times was I wondering if null
or string.Empty
or \r\n
is an authorized value for a method, and XML comments were silent on that!
In conclusion, without code contracts, lots of pieces of code are like that:
I'll accept some values but not others, but you'll have to guess or read the documentation, if any. Actually, don't read the documentation: it's outdated. Simply loop through all values and you'll see the ones which make me throw exceptions. You also have to guess the range of values which may be returned, because even if I would tell a bit more to you about them, it may not be true, given the hundreds of changes made to me for the last years.
With code contracts, it becomes:
The title argument can be a non-null string with a length of 0..500. The integer which follows is a positive value, which can be zero only when the string is empty. Finally, I'll return a IDefinition
object, never null.
Benefit 3: contracts of interfaces
A third benefit is that code contracts empower interfaces. Let's say you have something like:
public interface ICommittable
{
public ICollection<AtomicChange> PendingChanges { get; }
public void CommitChanges();
...
}
How would you, using only asserts and exceptions, guarantee that CommitChanges
can be called only when PendingChanges
is not empty? How would you guarantee that PendingChanges
is never null
?
Benefit 4: enforce the results of a method
Finally, the fourth benefit is to be able to Contract.Ensure
the results. What if, when writing a method which returns an integer, I want to be sure that the value is never inferior or equal to zero? Including five years later, after suffering from lots of changes from lots of developers? As soon as a method has multiple return points, Assert
s become a maintenance nightmare for that.
Consider code contracts not only as a mean of correctness of your code, but a stricter way to write code. In a similar way, a person who used exclusively dynamic languages may ask why would you enforce types at language level, while you can do the same thing in assertions when needed. You can, but static typing is easier to use, less error-prone compared to a bunch of assertions, and self-documenting.
The difference between dynamic typing and static typing is extremely close to the difference between ordinary programming and programming by contracts.