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 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:
Create a TypeLevelAspect which implements IAspectProvider.
Decorate
AutoDataContractAttribute
with the MulticastAttribute, and set the Inheritance toStrict
. Note thatMulticastInheritance.Strict
andMulticastInheritance.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 Understanding Aspect Lifetime and Scope .
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 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
:
// [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:
[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 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:
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 Adding Aspects to Multiple Declarations Using Attributes 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
MulticastAttributeUsageAttribute
TypeLevelAspect
InstanceLevelAspect
DataContractAttribute
DataMemberAttribute
IAspectProvider
MulticastAttribute
Inheritance
Other Resources