PostSharp 4.3 / / Post­Sharp Documentation / Standard Patterns / Contracts / Creating Custom Contracts
Creating Custom Contracts

Given the benefits that contracts provide over manually checking values and throwing exceptions in code, you will likely want to implement your own contracts to perform your own custom checks and handle your own custom types.

The following steps show how to implement a contract which throws an exception if a numeric parameter is zero:

To implement a contract throwing an exception if a numeric parameter is zero:

  1. Use the following namespaces: PostSharp.Aspects and PostSharp.Reflection.

  2. Derive a class from LocationContractAttribute and override the GetErrorMessage() method:

    C#
    public
                                   
                                  class
                                   NonZeroAttribute : LocationContractAttribute
    {
        
                                  public
                                   
                                  const
                                   
                                  string
                                   ErrorMessage = 
                                  "NonZeroErrorMessage"
                                  ;
    
        
                                  public
                                   NonZeroAttribute()
          : 
                                  base
                                  ()
        {
        }
    
        
                                  protected
                                   
                                  override
                                   
                                  string
                                   GetErrorMessage()
        {
            
                                  return
                                   
                                  "Value {2} must have a non-zero value."
                                  ;
        }
    }
                                
    Note Note

    The value returned by GetErrorMessage() method can contain formatting placeholders. See the remarks section for the LocationContractAttribute class for more information.

  3. Implement the ILocationValidationAspect interface in the new contract class which exposes the ValidateValue(T, String, LocationKind) method. Note that this interface must be implemented for each type that is to be handled by the contract. In this example, the contract will handle both int and uint, so the interface is implemented for both integer types. If additional integer types were to be handled by this class (e.g. long), then additional implementations of ILocationValidationAspect would have to be added:

    C#
    public
                                   
                                  class
                                   NonZeroAttribute : LocationContractAttribute, ILocationValidationAspect<
                                  int
                                  >, ILocationValidationAspect<
                                  uint
                                  >
    {
        
                                  public
                                   
                                  const
                                   
                                  string
                                   ErrorMessage = 
                                  "NonZeroErrorMessage"
                                  ;
    
        
                                  public
                                   NonZeroAttribute()
          : 
                                  base
                                  ()
        {
        }
    
        
                                  protected
                                   
                                  override
                                   
                                  string
                                   GetErrorMessage()
        {
           
                                  return
                                   
                                  "Value {2} must have a non-zero value."
                                  ;
        }
    
        
                                  public
                                   Exception ValidateValue(
                                  int
                                   
                                  value
                                  , 
                                  string
                                   name, LocationKind locationKind)
        {
           
                                  if
                                   (
                                  value
                                   == 
                                  0
                                  )
             
                                  return
                                   
                                  this
                                  .CreateArgumentOutOfRangeException(
                                  value
                                  , name, locationKind);
          
                                  else
                                  
             
                                  return
                                   
                                  null
                                  ;
        }
    
        
                                  public
                                   Exception ValidateValue(
                                  uint
                                   
                                  value
                                  , 
                                  string
                                   name, LocationKind locationKind)
        {
            
                                  if
                                   (
                                  value
                                   == 
                                  0
                                  )
              
                                  return
                                   
                                  this
                                  .CreateArgumentOutOfRangeException(
                                  value
                                  , name, locationKind);
            
                                  else
                                  
              
                                  return
                                   
                                  null
                                  ;
        }
    }
                                

    The ValidateValue(T, String, LocationKind) method takes in the value to test, the name of the parameter, property or field, and the usage (i.e. whether it’s a parameter, property, or field). The method must return an exception if a check fails, or null or if no exception is to be raised.

With the contract now created it can be used. For example, the following methods which calculate the modulus between two numbers, can use the contract defined above to ensure that neither of their input parameters are zero:

C#
bool
                         Mod([NonZero] 
                        int
                         number, [NonZero] 
                        int
                         dividend)
{
  
                        return
                         ((number % dividend) == 
                        0
                        );
}
                        


                        bool
                         Mod([NonZero] 
                        uint
                         number, [NonZero] 
                        uint
                         dividend)
{
  
                        return
                         ((number % dividend) == 
                        0
                        );
}