When you are using new tools that make also something at code level then it is good idea to check out what additions are made to code during compilation. Code contracts have simple syntax when we are writing code at Visual Studio but what happens after compilation? Are our methods same as they look in code or are they different after compilation? In this posting I will show you how code contracts look after compiling.
- Controlling randomizer using code contracts
- Using runtime checking of code contracts in Visual Studio 2010
- Code Contracts: Hiding ContractException
- Code Contracts: Unit testing contracted code
- Forcing code contracts through interface contracts
- Invariant code contracts – using class-wide contracts
- Code contracts and inheritance
- Enabling XML-documentation for code contracts
- Using Sandcastle to build code contracts documentation
- Code Contracts: How they look after compiling?
- Code Contracts: validating arrays and collections
In my previous examples about code contracts I used randomizer class with method called GetRandomFromRangeContracted.
public int GetRandomFromRangeContracted(int min, int max)
{
Contract.Requires<ArgumentOutOfRangeException>(
min < max,
"Min must be less than max"
);
Contract.Ensures(
Contract.Result<int>() >= min &&
Contract.Result<int>() <= max,
"Return value is out of range"
);
return _generator.Next(min, max);
}
Okay, it is nice to dream about similar code when we open our assembly with Reflector and disassemble it. But… this time we have something interesting. While reading this code don’t feel uncomfortable about the names of variables. This is disassembled code. .NET Framework internally allows these names. It is our compilators that doesn’t accept them when we are building our code.
public int GetRandomFromRangeContracted(int min, int max)
{
int Contract.Old(min);
int Contract.Old(max);
if (__ContractsRuntime.insideContractEvaluation <= 4)
{
try
{
__ContractsRuntime.insideContractEvaluation++;
__ContractsRuntime.Requires<ArgumentOutOfRangeException>(
min < max,
"Min must be less than max", "min < max");
}
finally
{
__ContractsRuntime.insideContractEvaluation--;
}
}
try
{
Contract.Old(min) = min;
}
catch (Exception exception1)
{
if (exception1 == null)
{
throw;
}
}
try
{
Contract.Old(max) = max;
catch (Exception exception2)
{
if (exception2 == null)
{
throw;
}
}
int CS$1$0000 = this._generator.Next(min, max);
int Contract.Result<int>() = CS$1$0000;
if (__ContractsRuntime.insideContractEvaluation <= 4)
{
try
{
__ContractsRuntime.insideContractEvaluation++;
__ContractsRuntime.Ensures(
(Contract.Result<int>() >= Contract.Old(min)) &&
(Contract.Result<int>() <= Contract.Old(max)),
"Return value is out of range",
"Contract.Result<int>() >= min && Contract.Result<int>() <= max");
}
finally
{
__ContractsRuntime.insideContractEvaluation--;
}
}
return Contract.Result<int>();
}
As we can see then contracts are not simply if-then-else checks and exceptions throwing. We can see that there is counter that is incremented before checks and decremented after these whatever the result of check was.
One thing that is annoying for me are null checks for exception1 and exception2. Is there really some situation possible when null is thrown instead of some instance that is Exception or that inherits from exception?
Conclusion
Code contracts are more complex mechanism that it seems when we look at it on our code level. Internally there are done more things than we know. I don’t say it is wrong, it is just good to know how our code looks after compiling. Looking at this example it is sure we need also performance tests for contracted code to see how heavy is their impact to system performance when we run code that makes heavy use of code contracts.
View Comments (5)
You need help from the compiler.
Think about the above example and inheritance. Contracts should be inherited. That's why they are more than asserts
> This is disassembled code. .NET Framework internally allows these names. It is our compilators that doesn’t accept them when we are building our code.
It has nothing to do with the framework. It is valid IL and hence the runtime accepts it. Further, you could develop a compiler that produces such names. Or you could use Reflection to emit such IL names.
That's what I'm saying. In IL it is possible to use whatever names you like. If you try something like this in VS IDE on C# or VB.NET then you are not allowed to use variable names like these.
Agree that the null checks in the handlers are bothersome. They are a technical device to keep FxCop from issuing spurious warnings.
Code contracts how they look after compiling.. Dandy :)