Skip to content

Latest commit

 

History

History
289 lines (223 loc) · 12.5 KB

File metadata and controls

289 lines (223 loc) · 12.5 KB
uid title product categories summary
aspect-inheritance
Adding Aspects to Derived Classes and Methods Using Attributes
postsharp
PostSharp;AOP;Metaprogramming
The document explains how to use PostSharp's aspect inheritance feature to apply aspects to derived classes and methods. It provides examples of applying aspects to derived types, setting inheritance on a per-usage basis, and applying aspects to new methods of derived types.

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.

Applying aspects to derived types

One way to implement aspect inheritance is to add a xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute custom attribute to your aspect class. Aspects that apply to types are typically derived from xref:PostSharp.Aspects.TypeLevelAspect or xref:PostSharp.Aspects.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 xref:PostSharp.Aspects.TypeLevelAspect, which applies a xref:System.Runtime.Serialization.DataContractAttribute attribute to the base and all derived classes, and a xref:System.Runtime.Serialization.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 xref:PostSharp.Aspects.TypeLevelAspect which implements xref:PostSharp.Aspects.IAspectProvider.

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

    [MulticastAttributeUsage( Inheritance = MulticastInheritance.Strict )]
        [PSerializable]
        public sealed class AutoDataContractAttribute : TypeLevelAspect, IAspectProvider
        {
           // Details skipped - see the full sample.
        }

    [!NOTE] Aspect classes need to be serializable. For further details, see xref:aspect-lifetime.

  3. Decorate your base class with AutoDataContractAttribute. The following snippet shows a base customer class and a derived customer class:

    [AutoDataContract]
          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 xref:System.Runtime.Serialization.DataContractAttribute and xref:System.Runtime.Serialization.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 xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute attribute from AutoDataContractAttribute:

// [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 xref:PostSharp.Extensibility.MulticastAttribute.AttributeInheritance property as shown here:

[AutoDataContract( 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:

[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 xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute attribute and specifying the inheritance type, or to set the xref:PostSharp.Extensibility.MulticastAttribute.AttributeInheritance property on the custom attribute usage.

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

class Document
           {        
               // Details skipped.

                  // This method will be traced.
               [TraceMethod( 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

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

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

[TraceMethod(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:

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:

[TraceMethod(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

Reference

xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute
xref:PostSharp.Aspects.TypeLevelAspect
xref:PostSharp.Aspects.InstanceLevelAspect
xref:System.Runtime.Serialization.DataContractAttribute
xref:System.Runtime.Serialization.DataMemberAttribute
xref:PostSharp.Aspects.IAspectProvider
xref:PostSharp.Extensibility.MulticastAttribute
xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.Inheritance
Other Resources

xref:attribute-multicasting