Monday, July 12, 2010

The previous post on Code Contracts was about Contract.Requires. That method comes with an overload, which is a generic one that takes an Exception type as the generic type parameter. The purpose of this method is mainly in line with that of the Contract.Requires method that I explained in the previous post here.

The original code sample contained a few ArgumentExceptions that were thrown if certain conditions were not met. The use of the Contract.Requires method made the code more succinct with less clutter, but the exceptions were nowhere to be found. In case the precondition is not met in the code that uses Contract.Requires, a ContractException will be thrown at runtime. Sometimes it is feasible though to throw a more specific exception, like the ArgumentExceptions that were thrown in the IF-THEN-THROW constructions.

Just to step back to the code we had initially:

    public string RepeatFirstCharacter(string input, int count)
    {
      if (input == null)
      {
        throw new ArgumentNullException("input");
      }

      if (input.Length < 1)
      {
        throw new ArgumentException("The input should have at least one character.", "input");
      }

      if (count <= 0)
      {
        throw new ArgumentOutOfRangeException("count");
      }

      return new string(input.ToCharArray(0, 1)[0], count);
    }

This code is very specific about the exceptions. About the ArgumentOutOfRangeException one could argue that it is actually not a correct exception to throw here, since there is no specific range, but beyond that, the exceptions serve a purpose. It is up to the requirements of the final code and build whether or not the exception must be of the  ArgumentException family, or it should be a more ‘general’ ContractException, since it involves the predefined contract. When it is necessary to have the existing exceptions, one should use the Contract.Requires<TException> overload. The example code would end up looking like this:

    public string RepeatFirstCharacter(string input, int count)
    {
      Contract.Requires<ArgumentNullException>(input != null);
      Contract.Requires<ArgumentException>(input.Length >= 0, "The input should have at least one character.");
      Contract.Requires<ArgumentOutOfRangeException>(count > 0);

      return new string(input.ToCharArray(0, 1)[0], count);
    }

Beyond the fact that the more specific exception is used on contract failure, all other points mentioned in the post on Contract.Requires, also apply to Contract.Requires<TException>. The overload has an additional caveat though. The exception type that is to be thrown must have a public constructor accepting a single string argument. If such a constructor can not be determined at runtime, an internal ContractException will be created and thrown instead.

To conclude the posts on preconditions, the next will handle about Contract.EndContractBlock(). Even though it is not specific for preconditions, it will only truly serve a purpose when it appears at the end of legacy preconditions that are stated in the form of IF-THEN-THROW.

That’s it for now.
Meile

posted @ 9:38 PM | Feedback (11)

This second post in the series on Code Contracts will deal with the Contract.Requires method. In the introduction, I already mentioned that the Contract.Requires method is used to express a precondition. Whenever something has to be validated upfront, the Contract.Requires method is the one to use.

In the old style programming, any precondition would be stated as an IF-THEN-THROW construction, like in the following example:

    public string RepeatFirstCharacter(string input, int count)
    {
      if (input == null)
      {
        throw new ArgumentNullException("input");
      }

      if (input.Length < 1)
      {
        throw new ArgumentException("The input should have at least one character.", "input");
      }

      if (count <= 0)
      {
        throw new ArgumentOutOfRangeException("count");
      }

      return new string(input.ToCharArray(0, 1)[0], count);
    }

Not only does this create a lot of clutter, it also expresses the conditions from the negative, exceptional point of view. Looking at the code, it is required for the input to be a non-null string with a length of 1 or more. But the conditional check is on a null string value, and a length smaller than 1. If we were to rewrite the code above with the use of Contract.Requires, we would end up with the following code:

    public string RepeatFirstCharacter(string input, int count)
    {
      Contract.Requires(input != null);
      Contract.Requires(input.Length >= 0);
      Contract.Requires(count > 0);

      return new string(input.ToCharArray(0, 1)[0], count);
    }

Now all the clutter has gone. The code is more succinct, and the conditions really reflect the contract in  a non-exceptional manner. Given the fact that in many software development cycles the specifications are mainly written in terms of requirements, it’s a good thing to have this also in the code. Since the Code Contract preconditions, post conditions, object invariants etc. can all be merged into the generated XML documentation, this also means the requirements will also appear in them as well.

Moreover, the contracts as defined using the IF-THEN-THROW style will only affect the current call. If the RepeatFirstCharacter method had been defined as a virtual method, any derived class that implements an override will have to check for the contract itself. When using Code Contracts, this is no longer necessary. The binary rewriter used by Code Contracts will make sure the contract is also exposed in the derived method as well as in the base method.

All this comes with a few caveats though. Besides the fact that Contract.Requires calls must be at the beginning of the method (which is quite logical given their nature), the contract must only reference members with at least the same visibility as the enclosing method. Since the contract is exposed to clients calling the method, this requirement makes a lot of sense. Another caveat is that on a derived method additional requirements can not be set. This is one that also makes a lot of sense, since the interface definition largely determines the contract. And on an override, the interface isn’t changed, so why should the contract?

This method should be used for those preconditions that do not enforce a particular exception to be thrown, e.g. for backward compatibility reasons. In case a particular exception should be thrown one would have to use Contract.Requires<TException>(Boolean) method instead. I will dig into that method in the next post in this series.

That’s it for now.
Meile

posted @ 10:28 AM | Feedback (2)


posts - 24, comments - 125, trackbacks - 0, articles - 0

Copyright © Meile Zetstra