Creating code contract invariant

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.

Getting ready

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.

How to do it…

  1. Before you go on, ensure that you have added the code contracts using statement to the top of your Recipes.cs class file:
    using System.Diagnostics.Contracts;
  2. Next, we will add a new class called 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
    {
        
    }
  3. Add the following 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; }
  4. We will now add a constructor to our 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;
    }

    Note

    If you create public properties, it is always a good practice to create them with private setters such as public int Value { get; private set; }.

  5. The next method we need to add is the contract invariant method. You can call this method any name you like, and in this example, it is called 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);
    }
  6. You can further extend the 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");
    }
  7. Finally, add another method that returns the date formatted as month/day/year:
    public string ReturnGivenMonthDayYearDate()
    {            
        return $"{_Month}/{_Day}/{_Year}";
    }
  8. When you are finished, your 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}";
        }
    }
  9. Head back to the console application and add the following using statement to your console application Program.cs file:
    using Chapter8;
  10. We will now add a new instance of our 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();
  11. Running your console application will result in the code contract invariant throwing an exception because the year passed to the constructor is in the past:
    How to do it…
  12. Let's modify our code by passing a valid year value to the constructor, but keep the rest of the parameter values the same:
    try
    {
        InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year, 13, 32);
        string returnedDate = oInv.ReturnGivenMonthDayYearDate();
    
        WriteLine(returnedDate);
    }
    catch (Exception ex)
    {
        WriteLine(ex.Message);                
    }
    ReadLine();
  13. Running the console application will again result in an exception message stating that the month value cannot be greater then 12:
    How to do it…
  14. Once again, modify the parameters passed to the method and supply a valid year and month value, but pass an invalid day value:
    try
    {
        InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year, 11, 32);
        string returnedDate = oInv.ReturnGivenMonthDayYearDate();
    
        WriteLine(returnedDate);
    }
    catch (Exception ex)
    {
        WriteLine(ex.Message);                
    }
    ReadLine();
  15. Running the console application again will result in the code contract invariant throwing an exception because the day is clearly wrong. No month contains 32 days:
    How to do it…
  16. Modify the parameters passed to the constructor again, and this time, add valid values for year, month, and day:
    try
    {
        InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year, 11, 25);
        string returnedDate = oInv.ReturnGivenMonthDayYearDate();
    
        WriteLine(returnedDate);
    }
    catch (Exception ex)
    {
        WriteLine(ex.Message);                
    }
    ReadLine();
  17. Because November 25, 2016 is a valid date (because the current year is 2016), the formatted date is returned to the console application window:
    How to do it…
  18. Let's mix things up a little by passing 29 February, 2017 to the constructor:
    try
    {
        InvariantClassState oInv = new InvariantClassState(DateTime.Now.Year + 1, 2, 29);
        string returnedDate = oInv.ReturnGivenMonthDayYearDate();
    
        WriteLine(returnedDate);
    }
    catch (Exception ex)
    {
        WriteLine(ex.Message);                
    }
    ReadLine();
  19. Again, the code contract invariant method throws an exception because 2017 is not a leap year:
    How to do it…

How it works…

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.15.43