Forcing code contracts through interface contracts

Sometimes we need a way to make different implementations of same interface follow same rules. One option is to duplicate contracts to all implementation but this is not good option because we have duplicated code then. The other option is to force contracts to all implementations at interface level. In this posting I will show you how to do it using interface contracts and contracts class.

Using code from previous example about unit testing code with code contracts I will go further and force contracts at interface level. Here is the code from previous example. Take a careful look at it because I will talk about some modifications to this code soon.

public interface IRandomGenerator
{
   
int Next(int min, int
max);
}

public class RandomGenerator : IRandomGenerator
{
   
private Random _random = new Random
();

   
public int Next(int min, int
max)
    {
       
return
_random.Next(min, max);
    }
}

public class Randomizer
{
   
private IRandomGenerator
_generator;

   
private
Randomizer()
    {
        _generator =
new RandomGenerator
();
    }

   
public Randomizer(IRandomGenerator
generator)
    {
        _generator = generator;
    }

   
public int GetRandomFromRangeContracted(int min, int
max)
    {
       
Contract.Requires<ArgumentOutOfRangeException
>(
            min < max,
           
"Min must be less than max"
        );

       
Contract
.Ensures(
           
Contract.Result<int
>() >= min &&
           
Contract.Result<int
>() <= max,
           
"Return value is out of range"
        );

       
return _generator.Next(min, max);
    }
}

If we look at the GetRandomFromRangeContracted() method we can see that contracts set in this method are applicable to all implementations of IRandomGenerator interface. Although we can write new implementations as we want these implementations need exactly the same contracts. If we are using generators somewhere else then code contracts are not with them anymore.

To solve the problem we will force code contracts at interface level.

NB! To make the following code work you must enable Contract Reference Assembly building from project settings.

Interface contracts and contracts class

Interface contains no code – only definitions of members that implementing type must have. But code contracts must be defined in body of member they are part of. To get over this limitation, code contracts are defined in separate contracts class. Interface is bound to this class by special attribute and contracts class refers to interface through special attribute.

Here is the IRandomGenerator with contracts and contracts class. Also I write simple fake so we can test contracts easily based only on interface mock.

[ContractClass(typeof(RandomGeneratorContracts))]
public interface IRandomGenerator
{
   
int Next(int min, int
max);
}
 
[
ContractClassFor(typeof(IRandomGenerator))]
internal sealed class RandomGeneratorContracts : IRandomGenerator
{
   
int IRandomGenerator.Next(int min, int
max)
    {
       
Contract.Requires<ArgumentOutOfRangeException
>(
                min < max,
               
"Min must be less than max"
            );
 
       
Contract
.Ensures(
           
Contract.Result<int
>() >= min &&
           
Contract.Result<int
>() <= max,
           
"Return value is out of range"
        );
 
       
return default(int
);
    }
}
 

public class RandomFake : IRandomGenerator
{
   
private int
_testValue;
 
   
public RandomGen(int
testValue)
    {
        _testValue = testValue;
    }
 
   
public int Next(int min, int
max)
    {
       
return _testValue;
    }
}

To try out these changes use the following code.

var gen = new RandomFake(3);

try
{
    gen.Next(10, 1);
}

catch (Exception
ex)
{
   
Debug
.WriteLine(ex.Message);
}

try
{
    gen.Next(5, 10);
}

catch (Exception
ex)
{
   
Debug.WriteLine(ex.Message);
}

Now we can force code contracts to all types that implement our IRandomGenerator interface and we must test only the interface to make sure that contracts are defined correctly.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    2 thoughts on “Forcing code contracts through interface contracts

    Leave a Reply

    Your email address will not be published. Required fields are marked *