Understanding Attribute Multicasting
Overview of the Multicasting Algorithm
Every multicast attribute class must be assigned a set of legitimate targets using the MulticastAttributeUsageAttribute custom attribute, which is the equivalent and complement of <xref:System.AttributeUsageAttribute> for multicast attributes. Multicast attributes can be applied to types, methods, fields, properties, events, and parameters. For instance, a caching aspect targets methods. A field validation aspect targets fields.
When a field-level multicast attribute is applied to a type, the attribute is implicitly applied to all fields of that type. When it is applied to an assembly, it is implicitly applied to all fields of that assembly.
The general rule is: when a multicast attribute is applied to a container, it is implicitly (and recursively) applied to all elements of that container.
The next table illustrates how this rule translates for different kinds of targets.
|Directly applied to
|Implicitly applied to
|Assembly or Module
|Types, methods, fields, properties, parameters, and events contained in this assembly or module.
|Methods, fields, properties, parameters, and events contained in this type.
|Property or Event
|Accessors of this property or event.
|This method and the parameters of this method.
Filtering Target Elements of Code
Note that the default behavior is maximalist: we apply the attribute to all contained elements. However, PostSharp provides a way to restrict the set of elements to which the attribute is multicast: filtering.
Both the attribute developer and the user of the aspect can specify filters.
Just like normal custom attributes should be decorated with the
[AttributeUsage] custom attribute, multicast custom attributes must be decorated by the
[MulticastAttributeUsage] attribute (see MulticastAttributeUsageAttribute). It specifies which are the valid targets of the multicast attributes.
For instance, the following piece of code specifies that the attribute
GuiThreadAttribute can be applied to instance methods. Aspect users experience a build-time error when trying to use this aspect on a constructor or static method.
[MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Instance)]
[AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class|AttributeTargets.Method, AllowMultiple = true)]
public class GuiThreadAttribute : MethodInterceptionAspect
// Details skipped.
Note the presence of the <xref:System.AttributeUsageAttribute> attribute in the sample above. It tells the C# or VB compiler that the attribute can be directly applied to assemblies, classes, constructors, or methods. But this aspect will never be eventually applied to an assembly or a class. Indeed, the MulticastAttributeUsageAttribute attribute specifies that the sole valid targets are methods. Furthermore, the
TargetMemberAttributes property establishes a filter that includes only instance methods.
Therefore, if the aspect is applied to a type containing an abstract method, the aspect will not be multicast to this method, nor to its constructors.
In addition to multicast filtering, consider using programmatic validation of aspect usage. Any custom attribute can implement IValidableAnnotation to implement build-time validation of targets. Aspects that derive from Aspect already implement these interfaces: your aspect can override the method CompileTimeValidate(Object).
As an aspect developer, you should enforce as many restrictions as necessary to ensure that your aspect is only used in the way you intended, and raise errors in other cases. Using an aspect in an unexpected way may result in runtime errors that are difficult to debug.
The attribute user can specify multicasting filters using specific properties of the MulticastAttribute class. To make it clear that these properties only impact the multicasting process, they have the prefix
Attribute. Only the attribute user can set the values of these properties.
As an aspect developer, do not set the values of these properties in the aspect construtor. Instead, use multicast filtering or programmatic validation of aspect usage described in the section Developer-Specified Filtering.
As an aspect user, it is important to understand that you can only apply aspects to elements of code that have been allowed by the developer of the aspect.
For instance, the following element of code adds a tracing aspect to all public methods of a namespace:
[assembly: Trace( AttributeTargetTypes="AdventureWorks.BusinessLayer.*", AttributeTargetMemberAttributes = MulticastAttributes.Public )]
The following table lists the filters available to users and developers of aspects:
|T:PostSharp.Extensibility.MulticastAttribute Property (for ;aspect users)
|T:PostSharp.Extensibility.MulticastAttributeUsageAttribute Property (for aspect developers)
|Restricts the kinds of targets (assemblies, classes, value types, delegates, interfaces, properties, events, properties, methods, constructors, parameters) to which the attribute can be indirectly applied.
|Wildcard expression or regular expression specifying to which assemblies the attribute is multicast.
|Determines whether the aspect can be applied to elements defined in a different assembly than the current one.
|Wildcard expression or regular expression filtering by name the type to which the attribute is applied, or the declaring type of the member to which the attribute is applied.
|Restricts the visibility of the type to which the aspect is applied, or of the type declaring the member to which the aspect is applied.
|Wildcard expression or regular expression filtering by name the member to which the attribute is applied.
|Restricts the attributes (visibility, virtuality, abstraction, literality, ...) of the member to which the aspect is applied.
|Wildcard expression or regular expression specifying to which parameter the attribute is multicast.
|Restricts the attributes (in/out/ref) of the parameter to which the aspect is applied.
|Specifies whether the aspect is propagated along the lines of inheritance of the target interface, class, method, or parameter (see Understanding Aspect Inheritance).
Whenever possible, do not rely on naming conventions to apply aspects (properties AttributeTargetTypes, AttributeTargetMembers and AttributeTargetParameters). This may work perfectly today, and break tomorrow if someone renames an element of code without being aware of the aspect.
Overriding Filtering Attributes
Suppose we have two classes
B being derived from
B can be decorated with MulticastAttributeUsageAttribute. However, since
B is derived from
A, filters on
B cannot be more permissive than filters on
In other words, the MulticastAttributeUsageAttribute custom attribute is inherited. It can be overwritten in derived classes, but derived class cannot enlarge the set of possible targets. They can only restrict it.
Similarly (and hopefully predictably), the aspect user is subject to the same rule: they can restrict the set of possible targets supported by the aspect, but cannot enlarge it.