Developing Contracts : Extending Framework

In this article, I discuss the fundamentals of developing and extending contracts for Arcana.Contract due to planned release v0.1

A contract, is a specification that, is validated for correctness on pre-conditions and post-conditions of a method, constructor or property. A failed validation of a pre-condition or post-condition will trigger an exception. As due to planned release v0.1 of framework, there are only a handful number of contracts developed to elaborate on the ideas and provide a sufficient library to be extended and built upon.

Since the ability to throw exceptions when a contract fails, is a mandatory part of every contract, interface Arcana.Contract.IContract is provided that exposes IContract.Exception property. This is a convention by the framework, that all contracts should inherit from this interface. This allows named property assignment, during initialization of contracts, so that the user can assign custom exceptions to deal with failed validations.

Nearly, all contracts perform some type of compare between values. The ability to represent a comparer to compare values is provided by Arcana.Contract.IComparingContract which exposes IComparingContract.Comparer property, which is of type System.Collections.IComparer interface. Due to limitations of c#, currently, there is no way of assigning a custom comparer using named properties. The default value, which an implementation should have for this property, should be System.Collections.Comparer.Default. Also all implementing classes should use IComparingContract.Comparer property for value comparing.

A seperate implementation of these two primary interfaces, is given for each of these fundamental interfaces.

Argument Contract Development

To develop and extend contracts that apply to arguments of methods, base classes Arcana.Contract.Argument.ContractBase and Arcana.Contract.Argument.ComparingContractBase are provided. All argument contracts should extend one of these two base classes. By convention, all implementers of these two abstract classes, should take as a constructor parameter, the name of the argument they are applied by a string value. Remember that, due to limitations of PostSharp, we are unable to supply aspects on method arguments. So these aspects, or contracts, have to be supplied with method definitions.
In order to validate an argument, All a developer has to do is to implement the abstract method, Argument.ContractBase.ValidateArgument method. The signature of this method is the following:

protected abstract bool ValidateProperty(object value);

This method takes as input, the value of the argument that needs to be validated, and returns a true/false value indicating that if the argument is validated or not. If the argument was validated, a value of true should be returned. The framework is responsible for generating exception, appointed by Argument.ContractBase.Exception named property.

It is important to note, that, all contracts that perform comparing operations, should use Argument.ComparingContractBase.Comparer property only.

As an example of contract development endeavor, a piece of code taken from the development source files is provided.

using System;
using System.Collections.Generic;
using System.Text;
using PostSharp.Aspects.Dependencies;

namespace Arcana.Contract.Argument.Is
{
    /// <summary>
    /// Represents a contract that adheres to <b>Argument is in</b> semantic.
    /// </summary>
    [Serializable]
    [ProvideAspectRole(Roles.Contract)]
    [ProvideAspectRole(Roles.Method)]
    [ProvideAspectRole(Roles.Argument)]
    [ProvideAspectRole(Roles.Conditional)]
    [AspectRoleDependency(AspectDependencyAction.Commute, Roles.Conditional)]
    public sealed class InAttribute : ComparingContractBase
    {
        private System.Collections.ICollection _collection;

        /// <summary>
        /// Initializes a new instance of <b>Argument.Is.InAttribute</b>.
        /// </summary>
        /// <param name="argumentName">The argument to which this contract is applied.</param>
        /// <param name="collection">The collection of items which <i>argumentName</i> should be a member of.</param>
        /// <exception cref="System.ArgumentNullException"><i>argumentName</i> or <i>collection</i> is <b>null</b>.</exception>
        public InAttribute(string argumentName, System.Collections.ICollection collection)
            : base(argumentName)
        {
            if (collection == null) throw new ArgumentNullException();
            _collection = collection;
        }

        /// <summary>
        /// Performs validation on the argument.
        /// </summary>
        /// <param name="argument">The argument that should be validated.</param>
        /// <returns><b>true</b> if <i>argument</i> is validated, otherwise <b>false</b>.</returns>
        protected override bool ValidateArgument(object argument)
        {
            foreach (var item in _collection)
            {
                if (Comparer.Compare(argument, item) == 0) return true;
            }
            return false;
        }
    }
}


The above contract, enforces the pre-condition that, the value of the argument should be a member of a collection of items. This is just like enforcing an enumeration on the argument.

Property Contract Development

As with the argument contract development endeavor, The base classes Arcana.Contract.Property.ContractBase and Arcana.Contract.Property.ComparingContractBase are provided. The abstract method, Property.ContractBase.ValidateProperty with following signature is responsible for validating the property value.

protected abstract bool ValidateProperty(object value);

Please note that, unlike developing contracts for method arguments, property contracts do not require the property name to operate.

Last edited Aug 31, 2010 at 6:03 AM by arcane_master, version 1

Comments

No comments yet.