Search code examples
c#code-contracts

Contract class references member which is not part of the abstract class/interface being annotated


Hi I'm Trying to make a simple code contract for a setter which states a DateTime Has to be at least fifteen years old.

If I do the validation in a member of the contract class the compiler yields

Contract class 'TypeValidation.CodeContract.CitizenContract' references member 'TypeValidation.CodeContract.CitizenContract.BeGreaterThanFiveTeenYearsOld(System.DateTime)' which is not part of the abstract class/interface being annotated.

My code is:

[ContractClass(typeof(CitizenContract))]
public interface ICitizen
{
    int Age { get; set; }
    DateTime BirtDate { get; set; }
    string Name { get; set; }
}

[ContractClassFor(typeof(ICitizen))]
public class CitizenContract : ICitizen
{
    public int Age
    {
        get { return default(int); }
        set
        {

            Contract.Requires<ArgumentOutOfRangeException>(value > 15, "Age must be sixteen or greater.");
        }
    }


    public DateTime BirtDate
    {
        get { return default(DateTime); }
        set
        {
            Contract.Requires<ArgumentOutOfRangeException>(MoreThanFifTeenYearsOld(value), "The birthdate has to be a minimum of sixteen years old");
        }
    }

    public string Name
    {
        get { return default(string); }
        set
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrWhiteSpace(value), "Name Cant be null or empty.");
            Contract.Requires<ArgumentOutOfRangeException>(value.Length >= 3 && value.Length <= 50, "Name has to have between three and fifty.");
        }
    }

    bool MoreThanFifTeenYearsOld(DateTime dateToValidate)
    {

        if (dateToValidate == default(DateTime)) return false;

        DateTime zeroTime = new DateTime(1, 1, 1);
        var currentTime = DateTime.Now;
        if (currentTime <= dateToValidate) return false;
        TimeSpan span = currentTime - dateToValidate;
        return ((zeroTime + span).Year - 1) >= 16;
    }
}

I don't understand why it complains, can anyone explain why? Thanks in advance


Solution

  • You cannot add new members, because the contracts are evaluated on arbitrary instances of ICitizen, not on instances of CitizenContract, and those don't have that method. Because your method does not actually need the instance, you can make it static. This is not enough to silence the error, but you can move the method to another class. Also, the method should be public and [Pure]:

    public static class CitizenContractHelpers {
        [Pure]
        public static bool MoreThanFifTeenYearsOld(DateTime dateToValidate) {
        …
        }
    }
    

    Also, the contract class should be abstract.

    Read the manual at http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf, there is everything you needed to know.

    This example is not a good use of Code Contracts, which should be used to find programming bugs. Validity of your contract depends on the environment DateTime.Now, which the programmer usually does not control (the user can change time on his computer while using your application). So in this case a simple if-throw check in the implemenations would be better.