Something that is defined as invariant tells us that it will never change. It will always be the same, no matter what. This brings up a vast array of use cases if we consider this in the context of code contracts. The invariant code contract is basically used to validate the internal state of a class. So, what do we mean by the "internal state?" Well, the properties of the class give that class a specific state. Let's assume that we wanted to guarantee that the properties of the class we are using only accept specific values, thereby assuring the internal state of that class. This is where the code contract invariant comes into play.
You can understand the use of the invariant better with the use of the following example. Assume that the class needs to store dates. We can't ever store a date in the past though. Any date used in the class must be a current or future date.
using
statement to the top of your Recipes.cs
class file:using System.Diagnostics.Contracts;
InvariantClassState
to the Recipes.cs
class file. This is so that we can create an instance class and not a static class:public class InvariantClassState { }
private
properties to your InvariantClassState
class that will accept integer values for the year, month, and day:private int _Year { get; set; } private int _Month { get; set; } private int _Day { get; set; }
InvariantClassState
class. The constructor will accept parameters to set the properties created earlier:public InvariantClassState(int year, int month, int day) { _Year = year; _Month = month; _Day = day; }
Invariants()
. You will read many developers stating that a commonly accepted practice is to call this method ObjectInvariant()
. The naming of this method, however, has no impact on the invariant code contract. You will notice that we decorate this method with [ContractInvariantMethod]
, and it is this that defines this method (whatever the name) as the invariant code contract. Another important thing to remember is that the invariant code contract method must be a void
method and be specified as a private
method.Inside our code contract invariant method, we now specify which properties are invariant. In other words, those properties that can never be any other value than what we specify inside this code contract invariant method. For starters, we will specify that the year value cannot be in the past. We will also ensure that the month value is a valid value between 1
and 12
. Finally, we will specify that the day value cannot be a value outside the days contained in the month supplied or a value less than 1
:
[ContractInvariantMethod] private void Invariants() { Contract.Invariant(this._Year >= DateTime.Now.Year); Contract.Invariant(this._Month <= 12); Contract.Invariant(this._Month >= 1); Contract.Invariant(this._Day >= 1); Contract.Invariant(this._Day <= DateTime.DaysInMonth(_Year, _Month); }
Contract.Invariant
methods by supplying an exception message. Your Invariants()
method will then look like this:[ContractInvariantMethod] private void Invariants() { Contract.Invariant(this._Year >= DateTime.Now.Year, "The supplied year is in the past"); Contract.Invariant(this._Month <= 12, $"The value {_Month} is not a valid Month value"); Contract.Invariant(this._Month >= 1, $"The value {_Month} is not a valid Month value"); Contract.Invariant(this._Day >= 1, $"The value {_Day} is not a valid calendar value"); Contract.Invariant(this._Day <= DateTime.DaysInMonth(_Year, _Month), $"The month given does not contain {_Day} days"); }
public string ReturnGivenMonthDayYearDate() { return $"{_Month}/{_Day}/{_Year}"; }
InvariantClassState
class will look like this:public class InvariantClassState { private int _Year { get; set; } private int _Month { get; set; } private int _Day { get; set; } public InvariantClassState(int year, int month, int day) { _Year = year; _Month = month; _Day = day; } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(this._Year >= DateTime.Now.Year, "The supplied year is in the past"); Contract.Invariant(this._Month <= 12, $"The value {_Month} is not a valid Month value"); Contract.Invariant(this._Month >= 1, $"The value {_Month} is not a valid Month value"); Contract.Invariant(this._Day >= 1, $"The value {_Day} is not a valid calendar value"); Contract.Invariant(this._Day <= DateTime.DaysInMonth(_Year, _Month), $"The month given does not contain {_Day} days"); } public string ReturnGivenMonthDayYearDate() { return $"{_Month}/{_Day}/{_Year}"; } }
using
statement to your console application Program.cs
file:using Chapter8;
InvariantStateClass
class and pass the values to the constructor. First, pass the current year less than 1
to the constructor. This will result in the last year being passed to the constructor:try { InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year - 1, 13, 32); string returnedDate = oInv.ReturnGivenMonthDayYearDate(); WriteLine(returnedDate); } catch (Exception ex) { WriteLine(ex.Message); } ReadLine();
try { InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year, 13, 32); string returnedDate = oInv.ReturnGivenMonthDayYearDate(); WriteLine(returnedDate); } catch (Exception ex) { WriteLine(ex.Message); } ReadLine();
12
:try { InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year, 11, 32); string returnedDate = oInv.ReturnGivenMonthDayYearDate(); WriteLine(returnedDate); } catch (Exception ex) { WriteLine(ex.Message); } ReadLine();
try { InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year, 11, 25); string returnedDate = oInv.ReturnGivenMonthDayYearDate(); WriteLine(returnedDate); } catch (Exception ex) { WriteLine(ex.Message); } ReadLine();
try { InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year + 1, 2, 29); string returnedDate = oInv.ReturnGivenMonthDayYearDate(); WriteLine(returnedDate); } catch (Exception ex) { WriteLine(ex.Message); } ReadLine();
The code contract invariant method is a simple yet effective way to ensure that the state of your class is not modified. You can then assume that the properties you use inside your class are always correct and will never contain unexpected values. We like to think of the code contract invariant as a type of immutable (which it isn't). Strings are immutable, which means that the original value is never modified when the value changes. A new space in memory is always created when you change the value of a string. Similarly, this reminds me of the properties defined as invariant. These property values can never change to values other than those defined by our code contract invariant method.