What is a contract?
Let's take a look at a very simple example:
Void Wordlist.insert (string word)
This function is responsible for inserting word in ascending order into the list of words in wordlist, which Word cannot assume null.
The above descriptive text is used to describe the behavior of this function. When the caller who uses the function sees these descriptive text, you know how the function should be invoked and the function behavior in different situations, in other words, the above description text simply describes a convention between the function caller and the callee, which is also called a contract (contracts). Contracts can generally be grouped into three categories, including:
1. Precondition: What conditions need to be met before a function call: For example, the argument Word cannot be considered null
2. Postcondition: What conditions do you need to meet after a function call: For example, the parameter word is added to the wordlist member M_wordlist, m_wordlist element number +1
3. Invariant: What are the conditions that always need to be met after a function call: For example, the words in m_wordlist are always in ascending order
The concept of contract design is Bertrand Meyer proposed, and in Eiffel programming language This book has a detailed description of the Eiffel language itself on the contract design support is also very good, interested friends can try and compare.
. NET 4.0 in the Contracts
With the introduction of support for contract design in. NET 4.0, let's take a look at how the above example should be written with the contract design feature in 4.0:
1:public void Wordlist.insert (string word)
2: {
3: codecontract.requires (word!= null);
4: codecontract.ensures (codecontract.oldvalue<int> (_words). Count) + 1 = _words. Count);
5: Codecontract.ensuresonthrow (codecontract.oldvalue<int> (_words). Count) = = _words. Count);
6:..
7:}
which
1. Contract.Requires is precondition
2. Contract.ensures is postcondition
3. Contract.ensuresonthrow is postcondition, and ensures is the difference is that this is in the case of throw needs to meet postcondition
As you can see, contracts is being explicitly placed in the code, not just descriptive text, so what's the benefit.
1. Runtime support provided: These contracts can be run, and once the condition is not met, will pop up like an Assert the same dialog box error, as follows:
2. Provide static analysis support: Through static analysis contracts, static analysis tools can easily grasp the various relevant information of the function, even can be used as IntelliSense
See here, some friends may have some questions: contracts can do a conditional check and pop up like an Assert dialog box, this and assert what is the difference. In fact, the difference between contracts and assert is mainly:
1. The intent of the contracts is clearer, by invoking different requires/ensures and so on, representing different types of conditions that are easier to understand and automate than the simple assert
2. The position of the contracts is more uniform, placing 3 different conditions at the beginning of the code, rather than being scattered at the beginning and end of the function, for easy lookup and analysis.
3. Different developers, different groups, different companies, different libraries may have their own assert, which greatly increases the difficulty of automated analysis and is not conducive to developers writing code. And contracts is supported directly by. NET 4.0, which is unified.
Of course, contracts and assert have some very similar places, such as contracts and assert can check errors at run time, and can be turned off in arbitrary code. Several different typical configurations that support contracts in VS:
1. Contracts_full: Open All contracts
2. Contracts_preconditions: Only precondition
3. Requirealways only: There are only requirealways. Requirealways's meaning will be mentioned later.
These options can be modified through the project's Code Contracts page, which is obtained by installing the Contracts Toolkit:
Here to remind you, before using the contracts feature, be sure to download the latest version of the Contracts Development Kit: http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
This toolkit provides a number of tools, documentation, and VS plug-ins that are required for a range of contracts. You will not be able to use the contracts feature without installing this kit.
Just now we talked about the two conditions of requires and ensures, and here are some of the most commonly used contracts in. NET 4.0:
1. Requires: Conditions that must be met at the entrance of a function
2. Ensures: Conditions that must be met at function exit
3. Invariants: Conditions that must be met at the exit of all member functions
4. Assertions: Conditions that must be met at a certain point
5. Assumptions: A condition that must be met at some point to reduce unnecessary warning messages
Among them, for invariant need to make a little explanation. Because invariant is required for every member function to play a role, it is obvious that if you put this condition at the end of each function can play this effect, but this is more stupid to do so.. NET 4.0 is done in this way: using a member function as a invariant, The Contractinvariantmethod attribute is marked above, and contracts.invariant is used to specify each invariant condition in this member function. In this way, invariant will work on each function. At the same time, this invariant method should also mark the Pureattribute property, indicating that the function does not have side effects (the object's state is not modified)
Contracts The Mysteries
See here, do not know some friends found no, whether it is ensures or invariant, their location is not the location where the code should exist. For ensures, it is a function of export conditions, then must be called at the time of export, but why ensures write in front of it. Similarly, invariant works on each function, and if you write a function individually as a invariant how do you guarantee it will be called by each function? In fact, these are all very reasonable questions. First of all, ensure and invariant this kind of writing is very reasonable, the reason has been mentioned before, the remaining question is,. NET how to ensure that these conditions are executed at the right time. In fact, at compile time, Contracts's toolkit has a gadget ccrewrite, which is responsible for adjusting the compiled binaries as follows:
As you can see, Ccrewrite is responsible for adjusting the position of various conditions, and ultimately makes the condition in the correct position.
Interface-level contracts
In addition to adding a contract to the method implementation, the interface can also join the contract. The interface first needs to add a contractclassattribute, pointing to the corresponding Contractclass:
1: [Contractclass (typeof (Ifoocontract))]
2:interface IFoo {
3: int Count {get;}
4: void put (int value);
5:}
The Contractclass then declares the Contractclassforattribute, which is IFoo Contractclass, and then explicitly implements IFoo and joins contract:
1: [Contractclassfor (typeof (IFoo))]
2:sealed class Ifoocontract:ifoo {
3: int Ifoo.count {
4: Get {
5: contract.ensures (0 <= contract.result<int> ());
6: Return default (int);//Dummy return
7: }
8:}
9:
10:void ifoo.put (int value)
11: {
: contract.requires (0 <= value);
13:}
A complete example
Now let's look at a complete example. In this example, Wordlist used contract.requires/ensures/ensuresonthrow/invariant and other contracts, also included a few bugs, the program is simpler, here is not to say more:
1:using System;
2:using System.Collections.Generic;
3:using System.Linq;
4:using System.Text;
5:using System.Diagnostics.Contracts;
6:
7:namespace Contractdemo
8: {
9: class Wordlist
%: {
One: private list<string> _words;
12:
: Public wordlist (int capacity)
: {
: contract.requires (Capacity > 0);
: _words = new list<string> (capacity);
: }
18:
: Public void Insert (string word)
: {
: contract.requires (word!= null);
Contract.ensures (contract.oldvalue<int>) (_words. Count) + 1 = _words. Count);
Contract.ensuresonthrow (_words, contract.oldvalue<int>. Count) = = _words. Count);
24:
: int i;
: For (i = 0; i < _words. Count; ++i)
: {
: int compare = String.Compare (Word, _words[i]);
if (Compare > 0)
: Break ;
" Else If" (compare = 0)
throw new ApplicationException ("Already exist!");
: }
34:
A: _words. Insert (i, word);
: }
37:
: [Contractinvariantmethod ()]
: internal void invariant ()
: {
: //No null string
: contract.invariant (Contract.forall (_words, W => w!= null));
43:
//Make sure the words are in ascending order
: contract.invariant (Isascending ());
: }
47:
: [Pure]
: internal BOOL Isascending ()
: {
I: bool isascending = true;
for (int i = 1; i < _words. Count; ++i)
%: {
if (String.Compare (_words[i-1], _words[i]) > 0)
: {
A: isascending = false;
: Break ;
: }
: }
60:
: Return isascending;
: }
In: }
64:
"Class Program"
A: {
$: static void Main (string[] args)
: {
: wordlist wordlist = new Wordlist (0);
M: wordlist.insert ("Hello");
: Wordlist.insert ("World");
: }
A: }
74:}