La versione 4.0 del framework permette di definire dei code contract sia per i membri di una classe, sia il contratto predefinito per una interfaccia mediante una serie di attributi. Parametrizzando opportunamente tali attributi, l'interfaccia può anche essere generica.
Si supponga di definire l'interfaccia IRepository<T> opportunamente decorata:
[ContractClass(typeof(RepositoryContract<>))]
public interface IRepository<T>
{
void Add(T item);
}
L'attributo ContractClass permette di specificare quale sia la classe che denifisce il contratto di default per tutte le implementazioni di IRepository. Si noti che nella parametrizzazione dell'attributo l'interfaccia è specificata come tipo generico, ma senza l'alias del tipo.
[ContractClassFor(typeof(IRepository<>))]
public class RepositoryContract<T> : IRepository<T>
{
public void Add(T item)
{
Contract.Requires<ArgumentNullException>(item
!= null, "item");
}
}
E' quindi ora possibile implementare una versione della classe Repository<T> in grado di trarre vantaggio dal suddetto contratto:
public class Repository<T> : IRepository<T>
{
public void Add(T item)
{
//codice applicativo
}
}
Per verificare l'effettivo funzionamento del contratto, è sufficiente definire il seguente test:
[TestMethod()]
[ExpectedException(typeof(ArgumentNullException))]
public void Repository_Add_Should_Throw_On_Null_Valued_Object()
{
Repository<Organization> repo = new Repository<Organization>();
repo.Add(null);
}