Multicasting in PostSharp is a feature of all aspects that enables targeting several declarations using a single custom attribute or XML element in the postsharp.config
configuration file. It is exposed by MulticastAttribute
, the ultimate base type of all aspect classes.
In Metalama, multicasting is not implemented as a core feature but as an extension. This decision was made because the goal of adding an aspect to multiple declarations is better achieved in Metalama using fabrics. Therefore, you might eventually decide not to use multicasting. For more details, see Adding many aspects simultaneously.
Multicasting in Metalama is provided for backward compatibility with PostSharp. The objective is not to require PostSharp users to change their business code when migrating to Metalama, but only their aspect code. Multicasting in Metalama is implemented by the Metalama.Extensions.Multicast namespace. The implementation of this namespace is open source.
Enabling multicasting for a simple aspect
If your aspect is based on OverrideMethodAspect or OverrideFieldOrPropertyAspect, you can enable multicasting by changing the base class to OverrideMethodMulticastAspect or OverrideFieldOrPropertyMulticastAspect respectively. No other changes should be required.
The process is slightly more complex if your aspect is derived from another base class.
Enabling multicasting for a blank aspect
Below are general instructions to add the multicasting feature to any aspect. You can verify these instructions by examining the source code of OverrideMethodMulticastAspect or OverrideFieldOrPropertyMulticastAspect.
Step 1. Derive your class from MulticastAspect and implement IAspect as appropriate
The simplest approach is for your aspect to derive from MulticastAspect instead of any other class.
The MulticastAspect class defines:
- all properties that simulate the
MulticastAttribute
class from PostSharp, such as AttributeTargetTypes or AttributeTargetMemberAttributes, and - a protected property Implementation that you can call from your derived classes to implement multicasting.
Your aspect must also implement the IAspect<T> interface for all relevant types of declarations:
- on the final declarations where the aspect is actually applied (i.e., performs some actual work), and
- on any intermediate declaration where the aspect does no work other than multicasting itself to select child declarations.
The MulticastAspect class already implements the IAspect<ICompilation>
and IAspect<INamedType>
interfaces and correctly implements the BuildAspect method. For the interfaces you implement yourself, you must implement BuildAspect
.
Step 2. Implement the BuildAspect methods
Your implementation of the BuildAspect method should only call the this.Implementation.BuildAspect* method. The arguments you need to pass depend on the kind of declaration of the implemented IAspect<T> interface:
- For intermediate declarations, pass a single argument: the IAspectBuilder<TAspectTarget>.
- For final declarations, pass the IAspectBuilder<TAspectTarget> and a delegate that performs the actual work. This delegate will be called unless the aspect is skipped due to AttributeExclude.
Example:
public void BuildAspect( IAspectBuilder<IMethod> builder )
{
this.Implementation.BuildAspect(
builder,
b => b.Override( nameof(this.TheTemplate) ) );
}
Step 3. Implement eligibility
If your aspect has eligibility requirements on the type to which it is applied, override the BuildEligibility method.
Instead of repeating this eligibility condition in the BuildEligibility
method for all final destinations of the aspect, you can call the BuildEligibility(INamedType)
method as follows:
public override void BuildEligibility( IEligibilityBuilder<INamedType> builder )
{
// Do not offer the aspect on static types.
builder.MustNotBeStatic();
}
public void BuildEligibility( IEligibilityBuilder<IMethod> builder )
{
// Include the conditions of the declaring type.
this.BuildEligibility( builder.DeclaringType() );
// Conditions that are specific to the method.
builder.MustNotBeAbstract();
}