PostSharp 4.3 / / Post­Sharp Documentation / Custom Patterns / Developing Custom Aspects / Developing Simple Aspects / Handling Exceptions
Handling Exceptions

Adding exception handlers to code requires the addition of try/catch statements which can quickly pollute code. Exception handling implemented this way is also not reusable, requiring the same logic to be implemented over and over where ever exceptions must be dealt with. Raw exceptions also present cryptic information and can often expose too much information to the user.

PostSharp provides a solution to these problems by allowing custom exception handling logic to be encapsulated into a reusable class, which is then easily applied as an attribute to all methods and properties where exceptions are to be dealt with.

This topic contains the following sections:

  • Intercepting an exception
  • Specifying the type of handled exceptions
  • Ignoring exceptions
  • Replacing exceptions
  • Displaying the method arguments on exception
Intercepting an exception

PostSharp provides the OnExceptionAspect class which is the base class from which exception handlers are to be derived from.

The key element of this class is the OnException(MethodExecutionArgs) method: this is the method where the exception handling logic (i.e. what would normally be in a catch statement) goes. A MethodExecutionArgs parameter is passed into this method by PostSharp; it contains information about the exception.

To create an OnExceptionAspect class:

  1. Derive a class from OnExceptionAspect.

  2. Apply the PSerializableAttribute to the class.

  3. Override OnException(MethodExecutionArgs) and implement your exception handling logic in this class.

The following snippet shows an example of an exception handler which watches for exceptions of any type, and then writes a message to the console when an exception occurs:

C#
[PSerializable]
                        

                        public
                         
                        class
                         PrintExceptionAttribute : OnExceptionAspect
{

    
                        public
                         
                        override
                         
                        void
                         OnException(MethodExecutionArgs args)
    {
        Console.WriteLine(args.Exception.Message);
    }
}
                      

Once created, apply the derived class to all methods and/or properties for which the exception handling logic is to be used, as shown in the following example:

C#
 
                        class
                         Customer
{
    
                        public
                         
                        string
                         FirstName { 
                        get
                        ; 
                        set
                        ; }
    
                        public
                         
                        string
                         LastName { 
                        get
                        ; 
                        set
                        ; }

    [PrintException]
    
                        public
                         
                        void
                         StoreName(
                        string
                         path)
    {
        File.WriteAllText( path, 
                        string
                        .Format( “{
                        0
                        } {
                        1
                        }”, 
                        this
                        .FirstName, 
                        this
                        .LastName ) );
    }

}
                      

Here PrintException will output a message when an exception occurs in trying to write text to a file.

Alternatively the attribute can be applied to the class itself as shown below, in which case the exception handler will handle exceptions for all methods and properties in the class:

C#
[PrintExceptionAttribute(
                        typeof
                        (IOException))]
    
                        class
                         Customer
    {
      .
      .
      .
    }
                      

See the section Adding Aspects to Multiple Declarations for details about attribute multicasting.

Specifying the type of handled exceptions

The GetExceptionType(MethodBase) method can be used to return the type of the exception which is to be handled by this aspect. Otherwise, all exceptions will be caught and handled by this class.

Note Note

The GetExceptionType(MethodBase) method is evaluated at build time.

In the following snippet, we updated the PrintExceptionAttribute aspect and added the possibility to specify from the custom attribute constructor which type of exception should be traced.

C#
[PSerializable]
    
                        public
                         
                        class
                         PrintExceptionAttribute : OnExceptionAspect
    {
        Type type;

    
                        public
                         PrintExceptionAttribute() : 
                        this
                        (
                        typeof
                        (Exception))
    {
    }

        
                        public
                         PrintExceptionAttribute (Type type)
            : 
                        base
                        ()
        {
            
                        this
                        .type = type;
        }

        
                        // Method invoked at build time.
                        

                                
                        // Should return the type of exceptions to be handled. 
                        

                                
                        public
                         
                        override
                         Type GetExceptionType(MethodBase method)
        {
            
                        return
                         
                        this
                        .type;
        }


        
                        public
                         
                        override
                         
                        void
                         OnException(MethodExecutionArgs args)
        {
            Console.WriteLine(args.Exception.Message);
        }
    }
                      

Example:

C#
 
                        class
                         Customer
{
    
                        public
                         
                        string
                         FirstName { 
                        get
                        ; 
                        set
                        ; }
    
                        public
                         
                        string
                         LastName { 
                        get
                        ; 
                        set
                        ; }

    [PrintException(
                        typeof
                        (IOException)]
    
                        public
                         
                        void
                         StoreName(
                        string
                         path)
    {
        File.WriteAllText( path, 
                        string
                        .Format( “{
                        0
                        } {
                        1
                        }”, 
                        this
                        .FirstName, 
                        this
                        .LastName ) );
    }

}
                      
Note Note

If the aspect needs to handle several types of exception, the GetExceptionType should return a common base type, and the OnException implementation should be modified to dynamically handle different types of exception.

Ignoring exceptions

The FlowBehavior member of MethodExecutionArgs in the exception handler’s OnException(MethodExecutionArgs) method, can be set to ignore an exception. Note however that ignoring exceptions is generally dangerous and not recommended. In practice, it’s only safe to ignore exceptions in event handlers (e.g. to display a message in a WPF form) and in thread entry points.

Exceptions can be ignored by setting the FlowBehavior to Return:

C#
[PSerializable]
                        

                        public
                         
                        class
                         PrintAndIgnoreExceptionAttribute : OnExceptionAspect
{

    
                        public
                         
                        override
                         
                        void
                         OnException(MethodExecutionArgs args)
    {
        Console.WriteLine(args.Exception.Message);
        args.FlowBehavior = FlowBehavior.Return;
    }
 }
                      

If a method returns a value then the ReturnValue member of args can be set to an object to return. For example, consider the following GetDataLength method in Customer which returns the number of characters read from a file:

C#
 
                        class
                         Customer
{
    [PrintException(
                        typeof
                        (IOException))]
    
                        public
                         
                        int
                         GetDataLength(
                        string
                         path)
    {
                        

                        return
                         File.ReadAllText(path).Length;
    }

}
                      

We can then modify the OnException(MethodExecutionArgs) method of PrintAndIgnoreExceptionAttribute to return an integer with a value of -1:

C#
 
                        public
                         
                        override
                         
                        void
                         OnException(MethodExecutionArgs args)
{
    Console.WriteLine(args.Exception.Message);
    args.FlowBehavior = FlowBehavior.Return;
    args.ReturnValue = 
                        -1
                        ;
}
                      
Replacing exceptions

Many times an exception must be exposed to the user, either by allowing the original exception to be rethrown, or by throwing a new exception. This can be done by setting FlowBehavior as follows:

  • FlowBehavior.RethrowException: rethrows the original exception after the exception handler exits. This is the default behavior for the OnException(MethodExecutionArgs) advise.

  • FlowBehavior.ThrowException: throws a new exception once the exception handler exits. This is useful when details of the original exception should be hidden from the user or when a more meaningful exception is to be shown instead. When throwing a new exception, a new exception object must be assigned to the Exception member of MethodExecutionArgs. The following snippet shows the creation of a new BusinessExceptionAttribute which throws a BusinessException containing a description of the cause:

    C#
    [PSerializable]
                                
    
                                public
                                 
                                sealed
                                 
                                class
                                 BusinesssExceptionAttribute : OnExceptionAspect
    {
        
                                public
                                 
                                override
                                 
                                void
                                 OnException(MethodExecutionArgs args)
        {
            .
            .
            .
            args.FlowBehavior = FlowBehavior.ThrowException;        
            args.Exception = 
                                new
                                 BusinessException(
                                "Bad Arguments"
                                , 
                                new
                                 Exception(
                                "One or more arguments were null. Use the id "
                                 + guid.ToString() + 
                                " for more information"
                                ));
       }
    }
                                
    
    
                                class
                                 BusinessException : Exception
    {
           
                                public
                                 BusinessException(
                                string
                                 message, Exception innerException) : 
                                base
                                (message, innerException)
           {            
           }
    }
                              
Displaying the method arguments on exception

When an exception is thrown, it can be useful to view and display the parameter values that were passed into the method where the exception occurred. These values can be retrieved by iterating through the Arguments property of the args parameter of the OnException(MethodExecutionArgs)method. In the following snippet, OnException(MethodExecutionArgs) has been modified to iterate through all exception values, and to concatenate them into a string. If a null value is encountered, then the code embeds the word null into the string. This string is then displayed as the message of the NullReferenceException which is rethrown:

C#
  
                        public
                         
                        override
                         
                        void
                         OnException(MethodExecutionArgs args)
  {
      
                        string
                         parameterValues = 
                        ""
                        ;

      
                        foreach
                         (
                        object
                         arg 
                        in
                         args.Arguments)
      {
          
                        if
                         (parameterValues.Length > 
                        0
                        )
          {
              parameterValues += 
                        ", "
                        ;
          }

          
                        if
                         (arg != 
                        null
                        )
          {
              parameterValues += arg.ToString();
          }
          
                        else
                        
          {
              parameterValues += 
                        "null"
                        ;
          }
      }

          Console.WriteLine( “Exception {
                        0
                        } 
                        in
                         {
                        1
                        }.{
                        2
                        } invoked with arguments {
                        3
                        }”, args.Exception.GetType().Name, args.Method.DeclaringType.FullName, args.Method.Name, parameterValues );
   }
}
                      
Note Note

The Arguments field of args cannot be directly viewed in the debugger. The Arguments field must be referenced by another object in order to be viewable in the debugger.

See Also