CHAPTER 4 CLR AND BCL CHANGES 88 Code Contracts Code contracts are a method for expressing constraints and assumptions within your code. They allow specification of complex rules that can be validated at both compile time and runtime. Code contracts are also supported in VS2008. NOTE Compile time or static verification is available only in the Premium/Ultimate editions of Visual Studio. This is a real shame because it will probably prevent widespread adoption of this great technology rather than encouraging users to purchase a more expensive edition of VisualStudio (similar to MSTest & VS2005?). Hopefully this is not permanent, and you will see static verification available in all future versions of Visual Studio. In addition to providing validation, code contracts can assist with code documentation and aiding understanding of a problem. Functionality is available to automatically remove contracts from production code and separate them into a separate assembly if third parties want to use them. Code contracts are part of Microsoft’s ongoing research project Spec #; Spec #’s developers say they have been influenced by the Eiffel, JML, and AsmL languages. CAUTION Code contracts are still in active development, so this functionality might change. Hello Code Contracts To ensure that values are not null, you have probably written code similar to the following many times: public void myFunction(string input) { if(input==null) throw new System.NullReferenceException("Input cannot be null"); } Or perhaps you utilized the debug or trace assert like so: Debug.Assert(input != null); Code contracts are superior to the previous methods because they do the following: • Allow the creation of more complex rules • Can help you write better code by getting you to think about constraints within your code • Can reduce/prevent side effects CHAPTER 4 CLR AND BCL CHANGES 89 • Can be validated at both compile time and runtime • Are easy to read • Can be interpreted by automated tools such as PEX (http://research.microsoft.com/ en-us/projects/Pex/) • Work with XML documentation generation • Unlike debug statements, can optionally be utilized in both debug and release builds • Can be separated into separate assemblies for third-party use Let’s now create a simple code contract to ensure that an input value is not null or equal to 5. Installing Code Contracts Although VS2010 Professional edition contains some of the assemblies required for code contracts, the team didn’t want to tie code contract development to the release of Visual Studio, so the Code Contract SDK is available as a separate download. CAUTION You can run the following code without downloading the SDK but the code contracts won't actually do anything. So make sure to download the SDK first. There are two versions of the SDK available (currently named Standard edition and TFS edition). The TFS edition is for Premium/Ultimate and contains compile time verification and some additional features. The Standard edition does not contain this full static verification, but will offer warnings if contracts are breached (in my experiments with Invariants and Pure methods). SDKs are available here: http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx. Example Code Contract Once you have installed the Code Contract SDK, create a new console application and then add the following using directive: using System.Diagnostics.Contracts; Now add the following code: static void Main(string[] args) { DoSomething(5); } CHAPTER 4 CLR AND BCL CHANGES 90 public static void DoSomething(int? Input) { Contract.Requires(Input != null); Contract.Requires(Input != 5); } Before you run the application, go to the Properties page of your project and select the new Code Contracts tab (see Figure 4-4). Figure 4-4. New code contract tab Check the box marked Perform Runtime Contract Checking and run the code. You should receive an error message similar to that shown in Figure 4-5. CHAPTER 4 CLR AND BCL CHANGES 91 Figure 4-5. Alert box showing failure of code contract assumption Enabling Code Contract Static Verification (Premium/Ultimate Edition Only) To enable static verification, you need to go into the project Properties screen, select the Code Contract tab, and ensure that the static checking option is selected. When you compile your applications now, you should see the contracts are checked at compile time. You might ask why static verification is not always on? One reason is that if you are writing unit tests, you might want to pass null values into your methods and ensure that your code handles them correctly. If static verification was always on, you could not run your unit tests. Contract Inheritance You should note that contracts are inherited between classes, but it is not possible to add additional preconditions. CHAPTER 4 CLR AND BCL CHANGES 92 Architecture Behind the scenes, code contracts rewrite the generated IL. At a high level, you can divide code contract architecture into three main components: • Static method (expresses assumptions and constraints; you will look at them shortly) • Binary rewriter (performs runtime checks) • Static checker (verifies assumptions at compile time; TFS edition only) Let’s now look at some of the different ways to declare assumptions in the code using the static methods available in code contracts. Conditions Code contracts allows you to create three types of conditions: • Preconditions • Postconditions • Invariants The following sections discuss some of the conditions you might want to utilize (this is by no means an exhaustive list and is being added to in each release). Preconditions Preconditions must be true at the start of a method. Contract.Assert In debug build, ensures that a condition is true. Contract.Assert(Input != null); Contract.Assume Used for static verification and tells code analysis tools to assume that a condition is true (for example, if you are calling a method you have written and you are sure it will never return a null result): Contract.Assume(Input!=null) Contract.Requires Ensures that a condition is true before subsequent code is run. The following will ensure that the input parameter is not null: Contract.Requires(input != null); CHAPTER 4 CLR AND BCL CHANGES 93 Contract.Requires has an overload that allows you to specify and exception type and message to be thrown: Contract.Requires<ArgumentNullException>(Input != null, "input"); Contract.EndContractBlock The Contract.EndContractBlock statement tells the compiler to treat code as a precondition and allows you to utilize legacy code without converting it to code contracts format: if (Input==null) throw new System.NullReferenceException("input is null"); Contract.EndContractBlock(); NOTE You cannot use EndContractBlock in conjunction with any other preconditions. Post Conditions Post conditions are conditions that are true at the exit of your method calls. Contract.Ensures Ensures that a condition is true on exit of method: Contract.Ensures(Output != 7); Contract.EnsuresOnThrow Ensures that a specific exception type is thrown for a condition: Contract.EnsuresOnThrow<System.IO.IOException>(Input != null); Contract.ForAll Allows the iteration through a set to ensure that all members meet a specific condition: Contract.ForAll(MySet, i=> i!=null); Object Invariants Object invariants allow you to specify conditions that must always be true for an object and are created by decorating a procedure with the [ContractInvariantMethod] attribute. The following code ensures that the ImportantData variable can never be null: CHAPTER 4 CLR AND BCL CHANGES 94 [ContractInvariantMethod] void MyInvariant() { Contract.Invariant(ImportantData !=null); } Code Contract Values Code contracts offer some useful pseudo variables that can be useful when writing your conditions. Contract.Result Contract.Result accesses a value in a condition that will be returned from a function without referring to it directly: Contract.Ensures(Contract.Result<Int32?>() >= -1); Contract.OldValue Contract.OldValue represents the values state at the start of the method call. OldValue performs a shallow copy of the specified variable and can be used to see whether a value has changed: Contract.Ensures(Input != Contract.OldValue(Input)); Pure Methods that are called within a contract should be decorated with the attribute [Pure], which indicates that the method has no side effects (doesn’t alter the state of any other objects). If you don’t add this attribute, you will receive a warning similar to this: Detected call to impure method 'ConsoleApplication6.Program.Multiply(System.Int32,System.Int32)' in a pure region in method To mark a method as pure, simply add the [Pure] attribute as follows: [Pure] public static double Multiply(int x, int y) { return x * y; } CHAPTER 4 CLR AND BCL CHANGES 95 Interface Contracts Because interfaces cannot have method bodies, contracts must be declared in a slightly different way: [ContractClass(typeof(ContractForInteface))] interface IDoSomething { int DoSomething(int value); } [ContractClassFor(typeof(IDoSomething))] sealed class ContractForInteface : IDoSomething { int IDoSomething.DoSomething(int value) { Contract.Requires( value != 0); //contracts require a dummy value return default(int); } PEX Developers interested in the code contracts features might also be interested in the PEX research project. PEX aims to automate testing by analyzing code. For more information, please refer to http://research.microsoft.com/en-us/projects/Pex/. Conclusion The changes to the CLR in this release have three main aims: • Improve performance • Offer better compatibility/control for applications built in previous versions of the .NET Framework • Introduce functionality necessary for enhancements such as Dynamic Language Runtime (DLR) and parallelization (refer to Chapter 3) The great news is that many of the low-level changes will make your applications run more quickly with no input required from you! A simplified security model is welcome, and it is good to see the addition of 64-bit support and APIs for working with some of the new Windows 7 features. A number of welcome tweaks have also been made to the BCL and NCL. Finally, you looked briefly at code contracts: a great method inspired outside of .NET to write safer and more reliable code. Great job, CLR and BCL team! CHAPTER 4 CLR AND BCL CHANGES 96 Further Reading • http://blogs.msdn.com/bclteam/ • http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0- beta-2.aspx • http://www.danielmoth.com/Blog/2008/11/new-and-improved-clr-4-thread-pool.html • http://blogs.msdn.com/ericeil/archive/2009/04/23/clr-4-0-threadpool- improvements-part-1.aspx • http://blogs.msdn.com/ukadc/archive/2009/10/13/background-and-foreground-gc- in-net-4.aspx • http://blogs.msdn.com/shawnfa/ • http://msdn.microsoft.com/en-us/library/dd233103(VS.100).aspx • http://download.microsoft.com/download/C/2/7/C2715F76-F56C-4D37-9231- EF8076B7EC13/userdoc.pdf • http://msdn.microsoft.com/en-us/magazine/ee677170.aspx . http://blogs.msdn.com/ncl/archive/ 200 9 /07 / 20/ new-ncl-features-in -net- 4- 0- beta-2.aspx • http://www.danielmoth.com/Blog/ 200 8/11/new-and-improved-clr -4- thread-pool.html • http://blogs.msdn.com/ericeil/archive/ 200 9/ 04 / 23/clr -4- 0- threadpool- improvements -part- 1.aspx. http://blogs.msdn.com/ericeil/archive/ 200 9/ 04 / 23/clr -4- 0- threadpool- improvements -part- 1.aspx • http://blogs.msdn.com/ukadc/archive/ 200 9/ 10/ 13/background-and-foreground-gc- in -net- 4. aspx • http://blogs.msdn.com/shawnfa/. expensive edition of Visual Studio (similar to MSTest & VS 200 5?). Hopefully this is not permanent, and you will see static verification available in all future versions of Visual Studio. In addition