Multicasting: Enhance a group of methods with just one attribute

by Petr H. on 09 Nov 2020

Attribute multicasting, in PostSharp, is a way to apply an aspect (such as method interception) to many types or methods with just one attribute instance. It’s at the core of the ability of PostSharp to reduce the number of lines of code in your project.

In the most basic use case, you annotate a class with a method interception/method boundary attribute and it’s multicast (applied) to all methods in that class, but in this blog post, we’ll go over some more advanced use cases as well.

Multicasting is included in all versions of PostSharp including the free PostSharp Community edition, and works also for community add-ins such as ToString and StructuralEquality.

What multicasting means

Suppose MyLog is a method boundary aspect that writes to standard output at the beginning and end of each target method. It could look like this:

[Serializable]
public class MyLog : OnMethodBoundaryAspect
{
  public override void OnEntry(MethodExecutionArgs args)
  {
    Console.WriteLine("Entering");
  }
  public override void OnExit(MethodExecutionArgs args)
  {
    Console.WriteLine("Leaving");
  }
}

A method boundary aspect can target methods only, so if you apply it to a type, PostSharp attribute multicasting will instead cause it to apply to all methods of that type.

In the following example, if you write the code on the left, PostSharp will transform your code at build time as though you actually wrote the code on the right, but you avoided some code duplication.

diagram

Some attributes that enhance your code work on types rather than methods or members. For example, the community add-in ToString applies on types and synthesizes a ToString method for them.

You can use multicasting here as well. If you apply the ToString attribute to your assembly, it will instead apply to all classes in that assembly.

diagram

Note how properties of the add-in are copied to the actual target classes as well.

More precise targeting

We’ve seen that with multicasting, attributes “cascade down” from assemblies to types to members.

diagram

(Whether an attribute is type level or method level is determined by MulticastAttributeUsage. You can see documentation for details.)

But it is possible to choose your targets more precisely. Each multicast attribute has a set of properties whose names begins with Attribute, such as AttributeTargetTypes, which affect multicasting. (Almost all add-ins and all PostSharp aspects are multicast attributes.)

Here’s the MulticastAttribute properties that I find the most useful:

I’ll show this on a couple of examples:

[assembly: MyLog(
 AttributeTargetTypes = "*ViewModel", 
 AttributeTargetMemberAttributes = MulticastAttributes.Public)]

This means “Apply MyLog to all public methods of types whose names end in ViewModel.”

[assembly: MyLog(AttributeTargetMembers = "regex:button[0-9]+_Click")]
public class Form1 : Form { ... }

This means “Apply MyLog to all OnClick handlers on Form1 for buttons with default names.”

Even more precise targeting with exclusion

You can also use AttributeExclude in combination with AttributePriority to exclude some targets that you’ve previously included in multicasting. All attributes are processed in the order of AttributePriority and what comes out at the end of this processing becomes the set of targets to which the attribute is applied.

Here’s an example where the first line applies the aspect to all public methods, and the second line excludes getters and setters from the set:

code with exclusions

For more description and details, see our documentation. Make sure not to accidentally use “AspectPriority” which is a different property from AttributePriority, and doesn’t affect multicasting.

Where to use multicasting

In general, multicasting is useful and safe when applying the aspect to an unintended target does not have grave impact on functionality or performance.

I find multicasting the most helpful in the following cases:

In some cases, not even multicasting will be precise enough for you. In those cases, you can use aspect providers or compile time validation. It is also possible to multicast aspects to subclasses using multicast inheritance.

Conclusion

We often say that one of the benefits of PostSharp is that it saves you from writing extra repetitive lines of code.

Multicasting is one of the ways that allows you to do that, by factoring out common code and applying it at the class or assembly level.

For more examples and help with multicasting, see our short summary on GitHub or our product documentation.

Comments

newsletter

Subscribe to our
newsletter

Stay up to date on all the latest releases, news and discounts.
Follow us on social media: