Introduction to Arcana.Contract Framework

Arcana.Contract offers declarative, out-of-the-box contracts to be applied in a declarative fashion. Arcana.Contract allows contracts to be specified on method arguments, return values and properties, whether methods and properties are instance or static. Contracts are specification of pre-condition and post-conditions that allow declarative, reusable and more importantly, testable and documentable specification of operations to be specified. The framework goal is to also allow contract specification by interfaces and external markup files in the project. This allows better embedding of business operations, modeling and analysis endeavor.
The main namespace of framework, Arcana.Contract is divided into 3 sub-namespaces. namely, Arcana.Contract.Argument, Arcana.Contract.Method and Arcana.Contract.Property.These namespaces have sub-namespaces that read in english, and complete an english sentence. This allows, more readability and saves the time of the programmer while using intellisense of the IDE. The following list points out the main 3 namespaces of the framework:
  • Arcana.Contract.Argument : Provides contracts that apply to arguments of methods. Due to the limitation proposed by PostSharp, these contracts should be applied to methods, and the argument for which they validate. We will go through an example to demonstrate argument contracts.
  • Arcana.Contract.Property : Provides contracts that apply to properties and fields.
  • Arcana.Contract.Method : Provides contracts that apply to method behavior and return values.

The above 3 namespaces, have a similar hierarchy, which can be read as a complete English sentence. We start by simple examples. Please remember that installing and referencing community edition of PostSharp is necessary to compile these examples.

Preventing Null Argruments

In this example, we use Arcana.Contract framework, to specify the inability to pass null values to a method as an argument. This is specified by an argument contract Argument.Is.Not.NullAttribute contract, which performs efficient run-time check to prevent null values to be passed as an argument to method invocations. This contract is applied easily. By only instantiating Arguemnt.Is.Not.NullAttribute and providing an argument name, we can specify a contract that does not allow null values passed for the specified argument. Let's see a complete example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication5
{
    class Program
    {
        [Arcana.Contract.Argument.Is.Not.Null("name")]
        public static void SayHello(string name)
        {
            Console.WriteLine("Hello, " + name);
        }

        static void Main(string[] args)
        {
            SayHello("Arcane_Master");
            try
            {
                SayHello(null);
            }
            catch (ArgumentNullException)
            {
                Console.WriteLine("SayHello(null) threw exception as expected");
            }
        }
    }
}

As you can see in the above example, when a null value is passed, automatically an System.ArgumentNullException is thrown by the framework. The framework also allows custom user-specified exceptions to be thrown, which is determined by Exception named property. This property exists in all framework contracts and is inherited from Arcana.Contract.IContract interface. The following example demonstrates how we can change the default exception appointed by the framework.

[Arcana.Contract.Argument.Is.Not.Null("name", 
            Exception = typeof(InvalidOperationException))]
public static void SayHello(string name)
{
     Console.WriteLine("Hello, " + name);
}

As you can see in the above example, using Exception name property inherited from Arcana.Contract.IContract interface, which is the base interface of all framework contracts, it is possible to override default exception thrown by framework contracts. Usually most of framework contracts throw System.ArgumentException, with the exception of a few contracts that check for null values. namely, these contracts are the following:
  • Arcana.Contract.Argument.Is.Not.NullAttribute
  • Arcana.Contract.Property.Is.Not.NullAttribute

As for another example, let's see how we can protect an indexer against invalid range.

Indexer Example

In this example, we use Arcana.Contract framework to protect an indexer against invalid index, and throw System.ArgumentOutOfRangeException if the index was negative. We apply Arcana.Contract.Argument.Is.GreaterEqual.ThanAttribute to the get/set methods of an indexer to specify our contract. Let's first see the example and will be discussing it in more detail after.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication5
{
    class Program
    {
        [Arcana.Contract.Argument.Is.GreaterEqual.Than("index", 0,
                Exception = typeof(ArgumentOutOfRangeException))]
        public int this[int index]
        {
            get
            {
                return index;
            }
        }

        static void Main(string[] args)
        {
            Program target = new Program();
            int value = target[0];
            try
            {
                value = target[-1];
            }
            catch (ArgumentOutOfRangeException)
            {
                Console.WriteLine("this[-1] threw exception as expected");
            }
        }
    }
}

As you can see, We have applied Argument.Is.GreaterEqual.ThanAttribute contract to the whole indexer to specify a contract on index argument. And, as all framework contracts support, we have appointed the exception we want to throw using Exception named property. The argument to which we want to specify a contract is the first argument to the contract, and the value which a compare occurs against is the second argument to contract. This contract is able to compare a broad range of types. By default System.Collections.Comparer.Default is used for comparing values. Ability to propose custom comparers is yet to be developed. See Unable to assign custom comparers due to C# limitation for more details.

Source Files:
Indexer.cs
Prevent Null.cs

Last edited Aug 30, 2010 at 11:32 PM by arcane_master, version 2

Comments

No comments yet.