Metalama 1.0 / / Metalama Documentation / Conceptual Documentation / Migrating from Post­Sharp / Migrating Attribute Multicasting

Migrating PostSharp Attribute Multicasting to Metalama

Multicasting, in PostSharp, is a feature of all aspects that allows you to target several declarations using a single custom attribute or a single XML in the postsharp.config configuration file. Multicasting, in PostSharp, is exposed by the MulticastAttribute, the of all aspect classes.

Multicasting is not implemented as a core feature in Metalama but as an extension. The reason for that decision is that the goal of adding an aspect to several declarations is better achieved in Metalama using fabrics. For details, see Adding Aspects in Bulk from a Fabric.

Multicasting is provided in Metalama for backward compatibility with PostSharp because of our objective not to require PostSharp users to change their business code when they migrate 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. Nothing else should be required.

If your aspect is derived from another base class, things are a bit more complex.

Enabling multicasting for a blank aspect

Here are general instructions to add the multicasting feature to any aspect. You can check for yourself in the source code of OverrideMethodMulticastAspect or OverrideFieldOrPropertyMulticastAspect that it follows these instructions.

Step 1. Derive your class from MulticastAspect and implement IAspect as appropriate

The easiest approach is for your aspect to derive from MulticastAspect instead of any other class.

The MulticastAspect class defines:

Your aspect must also implement the IAspect<T> interface for all relevant kinds of declarations:

  • on the final declarations on which the aspect is actually applied (i.e. does some actual work) and the intermediate declarations, and
  • on any intermediate declaration where the aspect does no other work than multicasting itself to select children declarations.

The MulticastAspect class already implements the IAspect<ICompilation> and IAspect<INamedType> interfaces and properly implements the BuildAspect method. For the interfaces that you implement yourself, you also have to implement BuildAspect.

Step 2. Implement the BuildAspect methods

The only thing that your implementation of the BuildAspect should be to 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 the intermediate declarations, you must pass a single argument: the IAspectBuilder<TAspectTarget>.
  • For the final declarations, pass the IAspectBuilder<TAspectTarget> and a delegate that does the actual work. This delegate will be called unless the aspect is skipped because of <xref:Metalama.Extensions.Multicast.MulticastAttribute.AttributeExclude>.

Example:

            public void BuildAspect( IAspectBuilder<IMethod> builder )
{
    this.Implementation.BuildAspect(
        builder,
        b => b.Advice.Override( b.Target, nameof(this.TheTemplate) ) );
}

          

Step 3. Implement eligibility

If your aspect has eligibility requirements on the type to which it is applied, override the BuildEligibility(INamedType) 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 like this:

            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();
}