Post­Sharp Documentation / Adding Aspects to Code / Adding Aspects Declaratively Using Attributes / Adding Aspects to Derived Classes and Methods Using Attributes

Adding Aspects to Derived Classes and Methods Using Attributes

By default, aspects apply to the class or class member which your attribute has been applied to. However, PostSharp provides the ability to specify aspect inheritance which can allow your attributes to be inherited in derived classes. This feature, named aspect inheritance can be specified on types, methods, and parameters, but not on properties or events.

Note Note

PostSharp Framework or higher edition is required for aspect inheritance.

This topic contains the following sections:

Applying aspects to derived types

One way to implement aspect inheritance is to add a MulticastAttributeUsageAttribute custom attribute to your aspect class. Aspects that apply to types are typically derived from TypeLevelAspect or InstanceLevelAspect.

The benefit of this approach is that the aspect will be automatically applied to all derived classes, eliminating the need to manually setup attributes in the derived classes. Moreover, this logic lives in one place.

The following steps describe how to enable aspect inheritance on existing aspect, derived from TypeLevelAspect, which applies a DataContractAttribute attribute to the base and all derived classes, and a DataMemberAttribute attribute to all properties of the base class and those of derived classes:

How to enable aspect inheritance on existing aspect:

  1. Create a TypeLevelAspect which implements IAspectProvider.

  2. Decorate AutoDataContractAttribute with the MulticastAttribute, and set the Inheritance to Strict. Note that MulticastInheritance.Strict and MulticastInheritance.Multicast have the same effect when applied to type-level aspects.

    C#
    [MulticastAttributeUsage(Inheritance = MulticastInheritance.Strict)]
    [PSerializable]
    public sealed class AutoDataContractAttribute : TypeLevelAspect, IAspectProvider
    {
       // Details skipped.
    }
  3. Decorate your base class with AutoDataContractAttribute. The following snippet shows a base customer class and a derived customer class:

    C#
    [AutoDataContractAttribute]
    class Document
    {
        public string Title { get; set; }
        public string Author { get; set; }
        public DateTime PublishedOn { get; set; }
    
    }
    
    class MultiPageArticle : Document
    {
        public List<ArticlePage> Pages { get; set; }
    }

When the attribute is applied to the base class, the DataContractAttribute and DataMemberAttribute attributes will be applied at compile time to both classes. If other derived classes were added, then these would be decorated automatically as well.

Setting inheritance on a per-usage basis

Specifying targets and attribute inheritance can also be done on a per-usage basis rather than hard-coding it into the custom attribute. In the following snippet, we’ve removed the MulticastAttributeUsageAttribute attribute from AutoDataContractAttribute:

C#
// [MulticastAttributeUsage(Inheritance = MulticastInheritance.Strict)]
[PSerializable]
public sealed class AutoDataContractAttribute : TypeLevelAspect, IAspectProvider
{
    // Details skipped.
}

Now the inheritance mode can be specified directly on the AutoDataContractAttribute instance by setting the AttributeInheritance property as shown here:

C#
[TraceMethodAttribute(AttributeInheritance = MulticastInheritance.Strict)]
class Document
{
    // Details skipped.
}
Applying aspects to overridden methods

The following example shows a custom attribute which when applied to a class, writes a message to the console window whenever a method enters and exits:

C#
[PSerializable]    
public sealed class TraceMethodAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine(string.Format("Entering {0}.{1}.", args.Method.DeclaringType.Name, args.Method.Name));
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Console.WriteLine(string.Format("Leaving {0}.{1}.", args.Method.DeclaringType.Name, args.Method.Name));
    }
}

Specifying inheritance is simply a matter of adding the MulticastAttributeUsageAttribute attribute and specifying the inheritance type, or to set the AttributeInheritance property on the custom attribute usage.

In the snippet below, we have added the TraceMethod aspect to a virtual method and used the AttributeInheritance property to require the aspect to be automatically applied to all overriding methods:

C#
class Document
{        
    // Details skipped.

       // This method will be traced.
    [TraceMethodAttribute(AttributeInheritance = MulticastInheritance.Strict)]
    public virtual void RenderHtml(StringBuilder html)
    {
         html.AppendLine( this.Title );            
       html.AppendLine( this.Author );
    }
}

class MultiPageArticle: Document
{

  // This method will be traced.
    public override void RenderHtml(StringBuilder html)
    {
      base.RenderHtml(html);    
      foreach ( ArticlePage page in this.Pages )
      {
        page.RenderHtml( html );
      }
    }

  // This method will NOT be traced.
       public void RenderHtmlPage(StringBuilder html, int pageIndex )
       {
          html.AppendFormat ( “{0}, page {1}”, this.Title, pageIndex+1 );            
          html.AppendLine();
          html.AppendLine( this.Author );

       }

   }

In this example, TraceMethodAttribute will output entry and exit messages for Document.RenderHtml method and MultiPageArcticle.RenderHtml method as shown here:

Entering MultiPageArcticle.RenderHtml
Entering Document.RenderHtml
Leaving Document.RenderHtml
Leaving MultiPageArcticle.RenderHtml
Note Note

Aspect inheritance works with virtual, abstract and interface methods and their parameters.

We would get the similar result by adding the TraceMethod attribute to the Document class. Indeed, by virtue of attribute multicasting (see section Adding Aspects to Multiple Declarations for more details), adding a method-level attribute to a class implicitly adds it to all method of this class.

C#
[TraceMethodAttribute(AttributeInheritance = MulticastInheritance.Strict)]
class Document
 {   
     // All property getters and setters will be traced.     
     public string Title { get; set; }
     public string Author { get; set; }
     public DateTime PublishedOn { get; set; }

        // This method will be traced.
     public virtual void RenderHtml(StringBuilder html)
     {
          html.AppendLine( this.Title );            
        html.AppendLine( this.Author );
     }
 }

 class MultiPageArticle: Document
 {
    // Property getters and setters will NOT be traced.
    public List<ArticlePage> Pages { get; set; }


   // This method will be traced.
     public override void RenderHtml(StringBuilder html)
     {
       base.RenderHtml(html);    
       foreach ( ArticlePage page in this.Pages )
       {
         page.RenderHtml( html );
       }
     }

   // This method will NOT be traced.
        public void RenderHtmlPage(StringBuilder html, int pageIndex )
        {
           html.AppendFormat ( “{0}, page {1}”, this.Title, pageIndex+1 );            
           html.AppendLine();
           html.AppendLine( this.Author );

        }

    }

However, by adding the TraceMethod aspect to all methods of the Document type, we added it to property getters and setters, influencing the output:

C#
Entering MultiPageArcticle.RenderHtml
Entering Document.RenderHtml
Entering Document.get_Title
Leaving Document.get_Title
Entering Document.get_Author
Leaving Document.get_Author
Leaving Document.RenderHtml
Leaving MultiPageArcticle.RenderHtml
Applying aspects to new methods of derived types

In the previous section the TraceMethod attribute used Strict inheritance which means that if the base class is decorated with the attribute, it will only be applied to methods which are declared in the base class and overridden in the derived class.

By changing the inheritance mode to Multicast, we specify that the aspect should be also be applied to new methods of the derived class, i.e. not only methods that are overridden from the base class.

In the following snippet we’ve changed inheritance from Strict to Multicast:

C#
[TraceMethodAttribute(AttributeInheritance = MulticastInheritance.Multicast)]
class Document
 {   
     // All property getters and setters will be traced.     
     public string Title { get; set; }
     public string Author { get; set; }
     public DateTime PublishedOn { get; set; }

        // This method will be traced.
     public virtual void RenderHtml(StringBuilder html)
     {
          html.AppendLine( this.Title );            
      html.AppendLine( this.Author );
     }
 }

 class MultiPageArticle: Document
 {
    // Property getters and setters will ALSO be traced.
    public List<ArticlePage> Pages { get; set; }


   // This method will be traced.
     public override void RenderHtml(StringBuilder html)
     {
       base.RenderHtml(html);    
       foreach ( ArticlePage page in this.Pages )
       {
         page.RenderHtml( html );
       }
     }

   // This method will ALSO be traced.
        public void RenderHtmlPage(StringBuilder html, int pageIndex )
        {
           html.AppendFormat ( “{0}, page {1}”, this.Title, pageIndex+1 );            
           html.AppendLine();
           html.AppendLine( this.Author );

        }

    }

With Strict inheritance in use, TraceMethodAttribute applied to Document was not applied to the RenderHtmlPage method and the Pages property. In other words, as the name suggests, Strict inheritance is strictly applying the attribute on base members and any derived members which are inherited. However, with Multicast inheritance, the aspect is also applied to the RenderHtmlPage method and the Pages property.

Strict inheritance evaluates multicasting and then inheritance, but Multicast inheritance evaluates inheritance and then multicasting.

See Also