I had read about Code Contracts long time ago, but I didn’t see a reason to use contract instead of simple tests and argument's check with Exception's throw
s. Only now, I’m trying to use them in real project. I didn’t see why I need to write:
Contract.Requires<ArgumentNullException>(argument == null);
Instead of like I did it before:
if (argument == null)
throw new ArgumentNullException(“argument”)
Couple of weeks ago, I had to reinstall my Windows 7 on my laptop (I bought SSD for my laptop). After I installed ReSharper with Visual Studio 2010 after I run, I accidentally chose “Go to the metadata” instead of the usual “Go to Object Explorer” by Ctrl and Mouse Click. So when I clicked on some class of System.xxx
(basic classes of .NET), I looked at the source code of these classes and found that they are using Code Contracts. I thought this is why I need to understand why I need to use contracts in my code, so I started to use them in some small applications and tried to find some interesting case.
You can look at my previous blog post and find that I used code contracts in this sample. Actually, it was first using code contracts. If you don’t know how to start using code contract in your project: you should go to the Microsoft Research site, download the last code contracts installer and it is better to download last Editor Extension as well. When you will do this, you will see two additional tabs in project properties window.
So, let's look at an example. We have class like this:
public sealed class HotKey
{
private IntPtr _handle;
public HotKey(Window window)
:this (new WindowInteropHelper(window))
{
}
public HotKey(WindowInteropHelper window)
:this(window.Handle)
{
}
public HotKey(IntPtr windowHandle)
{
_handle = windowHandle;
}
}
In this class, we are working with window’s handle, but we allow to create instance of this class not only with Handle
as parameters, but with Window
or WindowInteropHelper
objects as well. What will happen if user will try to create new HotKey
instance and put null
instead of Window
object (first constructor)? We will get an Exception with message “Value cannot be null. Parameter name:window.
”, this exception will throw constructor of type WindowInteropHelper
. So it is good luck that we will get an understandable message. But what about null
instead of WindowInteropHelper
(second constructor)? Try it. You will get NullReferenceException
with message “Object reference not set to an instance of and object
”. No good. How we can handle it? We can try to use separate method Initialize
:
public sealed class HotKey
{
private IntPtr _handle;
public HotKey(Window window)
{
if (window == null)
throw new ArgumentNullException("window");
Initialize(new WindowInteropHelper(window).Handle);
}
public HotKey(WindowInteropHelper window)
{
if (window == null)
throw new ArgumentNullException("window");
Initialize(window.Handle);
}
public HotKey(IntPtr windowHandle)
{
Initialize(windowHandle);
}
private void Initialize(IntPtr windowHandle)
{
_handle = windowHandle;
}
}
And also, you can use contracts:
public sealed class HotKey
{
private IntPtr _handle;
public HotKey(Window window)
: this (new WindowInteropHelper(window))
{
Contract.Requires(window != null);
}
public HotKey(WindowInteropHelper window)
: this(window.Handle)
{
Contract.Requires(window != null);
}
public HotKey(IntPtr windowHandle)
{
_handle = windowHandle;
}
}
Looks like that if you will pass null
instead of WindowInteropHelper
in the second constructor, you will get NullReferenceException
again, because first it will execute code in base constructor and next constructor’s body which we invoke. And R# tell us that we don’t need to check window!=null
, because we used this variable before considering that it cannot be null
(I created bug in ReSharper Youtrack):
Actually, the contracts works before method execution, so we will see Precondition failed exception:
Also, if you want to see ArgumentNullException
instead of ContractException
, you can write:
public sealed class HotKey
{
private IntPtr _handle;
public HotKey(Window window)
: this (new WindowInteropHelper(window))
{
Contract.Requires<ArgumentNullException>(window != null);
}
public HotKey(WindowInteropHelper window)
: this(window.Handle)
{
Contract.Requires<ArgumentNullException>(window != null);
}
public HotKey(IntPtr windowHandle)
{
_handle = windowHandle;
}
}
Or:
public sealed class HotKey
{
private IntPtr _handle;
public HotKey(Window window)
:this (new WindowInteropHelper(window))
{
if (window == null)
throw new ArgumentNullException("window");
Contract.EndContractBlock();
}
public HotKey(WindowInteropHelper window)
: this(window.Handle)
{
if (window == null)
throw new ArgumentNullException("window");
Contract.EndContractBlock();
}
public HotKey(IntPtr windowHandle)
{
_handle = windowHandle;
}
}
So all next code only with contracts!