(Also posted on the MSDN forum - but that doesn't get much traffic, as far as I can see.)
I've been trying to provide an example of Assert
and Assume
. Here's the code I've got:
public static int RollDice(Random rng) { Contract.Ensures(Contract.Result<int>() >= 2 && Contract.Result<int>() <= 12); if (rng == null) { rng = new Random(); } Contract.Assert(rng != null); int firstRoll = rng.Next(1, 7); Contract.Assume(firstRoll >= 1 && firstRoll <= 6); int secondRoll = rng.Next(1, 7); Contract.Assume(secondRoll >= 1 && secondRoll <= 6); return firstRoll + secondRoll; }
(The business about being able to pass in a null reference instead of an existing Random
reference is purely pedagogical, of course.)
I had hoped that if the checker knew that firstRoll
and secondRoll
were each in the range [1, 6]
, it would be able to work out that the sum was in the range [2, 12]
.
Is this an unreasonable hope? I realise it's a tricky business, working out exactly what might happen... but I was hoping the checker would be smart enough :)
If this isn't supported now, does anyone here know if it's likely to be supported in the near-ish future?
EDIT: I've now found that there are very complicated options for arithmetic in the static checker. Using the "advanced" text box I can try them out from Visual Studio, but there's no decent explanation of what they do, as far as I can tell.
Code contracts provide a way to specify preconditions, postconditions, and object invariants in . NET Framework code. Preconditions are requirements that must be met when entering a method or property. Postconditions describe expectations at the time the method or property code exits.
NET 4.0. Code Contracts API includes classes for static and runtime checks of code and allows you to define preconditions, postconditions, and invariants within a method.
I've had an answer on the MSDN forum. It turns out I was very nearly there. Basically the static checker works better if you split out "and-ed" contracts. So, if we change the code to this:
public static int RollDice(Random rng) { Contract.Ensures(Contract.Result<int>() >= 2); Contract.Ensures(Contract.Result<int>() <= 12); if (rng == null) { rng = new Random(); } Contract.Assert(rng != null); int firstRoll = rng.Next(1, 7); Contract.Assume(firstRoll >= 1); Contract.Assume(firstRoll <= 6); int secondRoll = rng.Next(1, 7); Contract.Assume(secondRoll >= 1); Contract.Assume(secondRoll <= 6); return firstRoll + secondRoll; }
That works without any problems. It also means the example is even more useful, as it highlights the very point that the checker does work better with separated out contracts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With