If you are not familiar with the Code Contracts library which is coming out of Microsoft R&D labs, you need to check this pretty cool little library out. As of Vs2010/.Net 4.0 this library will be making the jump out of the R&D labs.
Over the next few blog posts we will be taking a look at the topics below.
So the one of the first questions that many people ask when talking about using the Code Contracts library is ‘what do I do about my legacy code?’. Asking this is a very valid question. If your team has already subscripted to the design-by-contract or fail-fast concepts then maybe you have already added in your own checks. If this is the case the contracts team has thought of you and has already provided a simple answer.
Lets assume you have the following code:
public ReportCard GetDailyReportCard(Int32 employeeId, DateTime dateForReportCard )
{
if ( employeeId <= 0 ){ throw new ArgumentOutOfRangeException("employeeId", "The provided employee ID was out of range");}
if (dateForReportCard.Date >= DateTime.Now.AddDays(1).Date) { throw new ArgumentOutOfRangeException("dateForReportCard", "The provided date was in the future"); }
// do something meaning
var reportCard = new ReportCard(employeeId);
return reportCard;
}
As you can see, we are already doing a contract check, but we want to be able to take advantage of what the contracts library has to offer, so what do we do? Simple, we add in the usage of the Contract.EndContractBlock. When you put this call AFTER the last if-then-throw check in your method the contracts library assumes that all those calls are ‘legacy-requires’ and will weave the correct code in their place.
Here is the code with the correct contracts call added:
public ReportCard GetDailyReportCard(Int32 employeeId, DateTime dateForReportCard )
{
if ( employeeId <= 0 ){ throw new ArgumentOutOfRangeException("employeeId", "The provided employee ID was out of range");}
if (dateForReportCard.Date >= DateTime.Now.AddDays(1).Date) { throw new ArgumentOutOfRangeException("dateForReportCard", "The provided date was in the future"); }
Contract.EndContractBlock(); // marks this as legacy
// do something meaning
var reportCard = new ReportCard(employeeId);
return reportCard;
}
Now that we have added the call to EndContractBlock we should take a look at what the code weaver for the library did to our assembly post compilation.
public ReportCard GetDailyReportCard(int employeeId, DateTime dateForReportCard)
{
if (__ContractsRuntime.insideContractEvaluation <= 4)
{
try
{
bool _preConditionHolds;
__ContractsRuntime.insideContractEvaluation++;
if (employeeId <= 0)
{
_preConditionHolds = false;
}
else
{
_preConditionHolds = true;
}
__ContractsRuntime.Requires(_preConditionHolds, null, "employeeId > 0");
if (dateForReportCard.Date >= DateTime.Now.AddDays(1.0).Date)
{
_preConditionHolds = false;
}
else
{
_preConditionHolds = true;
}
__ContractsRuntime.Requires(_preConditionHolds, null, "!(dateForReportCard.Date >= DateTime.Now.AddDays(1).Date)");
}
finally
{
__ContractsRuntime.insideContractEvaluation--;
}
}
return new ReportCard(employeeId);
}
As you can see, the newly created code looks a little like our original code, but the weaver has done its magic and added in all the correct logic for you to turn your legacy contracts into ‘usable’ contracts by the library. So long story short, if you have legacy code and you want to take advantage of the contracts library you simply need to add a single line of code AFTER your last if-then-throw statement.
Till next time,
Posted
06-09-2009 6:51 AM
by
Derik Whittaker