PostSharp 4.3 / / Post­Sharp Documentation / Standard Patterns / Adding Aspects to Code / Adding Aspects Declaratively Using Attributes / Overriding and Removing Aspect Instances
Overriding and Removing Aspect Instances

Having multiple instances of the same aspect on the same element of code is sometimes a desired behavior. With multicasting custom attributes (MulticastAttribute), it is easy to end up with that situation. Indeed, many multicasting paths can lead to the same target.

However, most of the time, a different behavior is preferred. We could define a method-level aspect on the type (this aspect would apply to all methods) and override (or even exclude) the aspect on a specific method.

The multicasting engine has both the ability to apply multiple aspect instances on the same target, and the ability to replace or remove custom attributes.

Understanding the Multicasting Algorithm

Before going ahead, it is important to understand the multicasting algorithm. The algorithm relies on a notion of order of processing of aspect instances.

Important note Important

This section covers how PostSharp handles multiple instances of the same aspect type for the sole purpose of computing how aspect instances should be overridden or removed. See Coping with Several Aspects on the Same Target to understand how to cope with multiple instances of different aspects.

The following rules apply:

  1. Aspect instances defined on a container (for instance a type) have always precedence over instances defined on an item of that container (for instance a method). Elements of code are processed in the following order: assembly, module, type, field, property, event, method, parameter.

  2. When multiple aspect instances are defined on the same level, they are sorted by increasing of value of the AttributePriority.

The algorithm builds a list of aspect instances applied (directly and indirectly) on an element of code, sorts these instances, and processes overrides or removals as described below.

Applying Multiple Instances of the Same Aspect

The property MulticastAttributeUsageAttribute.AllowMultiple determines whether multiple instances of the same aspect are allowed on an element of code. By default, this property is set to true for all aspects.

In the following example, the methods in type MyClass are enhanced by one, two and three instances of the Trace aspect (see code comments).

C#
using
                         System;
                        

                        using
                         System.Diagnostics;
                        

                        using
                         PostSharp.Aspects;
                        

                        using
                         PostSharp.Extensibility;
                        

                        using
                         PostSharp.Serialization;
                        

                        using
                         Samples3;

[assembly: Trace(AttributeTargetTypes = 
                        "Samples3.My*"
                        , Category = 
                        "A"
                        )]
[assembly: Trace(AttributeTargetTypes = 
                        "Samples3.My*"
                        ,
    AttributeTargetMemberAttributes = MulticastAttributes.Public, Category = 
                        "B"
                        )]
                        


                        namespace
                         Samples3
{
    [PSerializable]
    
                        public
                         
                        sealed
                         
                        class
                         TraceAttribute : OnMethodBoundaryAspect
    {
        
                        public
                         
                        string
                         Category { 
                        get
                        ; 
                        set
                        ; }

        
                        public
                         
                        override
                         
                        void
                         OnEntry(MethodExecutionArgs args)
        {
            Trace.WriteLine(
                        "Entering "
                         +
                            args.Method.DeclaringType.FullName + 
                        "."
                         + args.Method.Name, 
                        this
                        .Category);
        }
    }


    
                        public
                         
                        class
                         MyClass
    {
        
                        // This method will have 1 Trace aspect with Category set to A.
                        

                                
                        private
                         
                        void
                         Method1()
        {
        }

        
                        // This method will have 2 Trace aspects with Category set to A, B
                        

                                
                        public
                         
                        void
                         Method2()
        {
        }

        
                        // This method will have 3 Trace aspects with Category set to A, B, C.
                        

                                [Trace(Category = 
                        "C"
                        )]
        
                        public
                         
                        void
                         Method3()
        {
        }
    }
}
                      
Overriding an Aspect Instance Manually

You can require an aspect instance to override any previous one by setting the aspect property AttributeReplace. This is equivalent to a deletion followed by an insertion (see below).

In the following examples, the first two methods of type MyClass are enhanced by aspects applied on assembly level, but these aspects are replaced by a different one on Method3.

C#
using
                         System;
                        

                        using
                         System.Diagnostics;
                        

                        using
                         PostSharp.Aspects;
                        

                        using
                         PostSharp.Extensibility;
                        

                        using
                         PostSharp.Serialization;
                        

                        using
                         Samples5;

[assembly: Trace(AttributeTargetTypes = 
                        "Samples5.My*"
                        , Category = 
                        "A"
                        )]
[assembly: Trace(AttributeTargetTypes = 
                        "Samples5.My*"
                        ,
    AttributeTargetMemberAttributes = MulticastAttributes.Public, Category = 
                        "B"
                        )]
                        


                        namespace
                         Samples5
{
    [PSerializable]
    
                        public
                         
                        sealed
                         
                        class
                         TraceAttribute : OnMethodBoundaryAspect
    {
        
                        public
                         
                        string
                         Category { 
                        get
                        ; 
                        set
                        ; }

        
                        public
                         
                        override
                         
                        void
                         OnEntry(MethodExecutionArgs args)
        {
            Trace.WriteLine(
                        "Entering "
                         +
                            args.Method.DeclaringType.FullName + 
                        "."
                         + args.Method.Name, 
                        this
                        .Category);
        }
    }


    
                        public
                         
                        class
                         MyClass
    {
        
                        // This method will have 1 Trace aspect with Category set to A.
                        

                                
                        private
                         
                        void
                         Method1()
        {
        }

        
                        // This method will have 2 Trace aspect with Category set to A, B.
                        

                                
                        public
                         
                        void
                         Method2()
        {
        }

        
                        // This method will have 1 Trace aspects with Category set to C.
                        

                                [Trace(Category = 
                        "C"
                        , AttributeReplace = 
                        true
                        )]
        
                        public
                         
                        void
                         Method3()
        {
        }
    }
}
                      
Overriding an Aspect Instance Automatically

To cause a new aspect instance to automatically override any previous one, the aspect developer must disallow multiple instances by annotating the aspect class with the custom attribute MulticastAttributeUsageAttribute and setting the property AllowMultiple to false.

In the following example, the methods in type MyClass are enhanced by a single Trace aspect:

C#
using
                         System;
                        

                        using
                         System.Diagnostics;
                        

                        using
                         PostSharp.Aspects;
                        

                        using
                         PostSharp.Extensibility;
                        

                        using
                         PostSharp.Serialization;
                        

                        using
                         Samples4;

[assembly: Trace(AttributeTargetTypes = 
                        "Samples4.My*"
                        , AttributePriority = 
                        1
                        , Category = 
                        "A"
                        )]
[assembly: Trace(AttributeTargetTypes = 
                        "Samples4.My*"
                        ,
    AttributeTargetMemberAttributes = MulticastAttributes.Public, AttributePriority = 
                        2
                        , Category = 
                        "B"
                        )]
                        


                        namespace
                         Samples4
{
    [MulticastAttributeUsage(MulticastTargets.Method, AllowMultiple = 
                        false
                        )]
    [PSerializable]
    
                        public
                         
                        sealed
                         
                        class
                         TraceAttribute : OnMethodBoundaryAspect
    {
        
                        public
                         
                        string
                         Category { 
                        get
                        ; 
                        set
                        ; }

        
                        public
                         
                        override
                         
                        void
                         OnEntry(MethodExecutionArgs args)
        {
            Trace.WriteLine(
                        "Entering "
                         +
                            args.Method.DeclaringType.FullName + 
                        "."
                         + args.Method.Name, 
                        this
                        .Category);
        }
    }


    
                        public
                         
                        class
                         MyClass
    {
        
                        // This method will have 1 Trace aspect with Category set to A.
                        

                                
                        private
                         
                        void
                         Method1()
        {
        }

        
                        // This method will have 1 Trace aspects with Category set to B.
                        

                                
                        public
                         
                        void
                         Method2()
        {
        }

        
                        // This method will have 1 Trace aspects with Category set to C.
                        

                                [Trace(Category = 
                        "C"
                        )]
        
                        public
                         
                        void
                         Method3()
        {
        }
    }
}
                      
Deleting an Aspect Instance

The MulticastAttribute.AttributeExclude property removes any previous instance of the same aspect on a target.

This is useful, for instance, when you need to exclude a target from the matching set of a wildcard expression. For instance:

C#
[assembly: Configurable( AttributeTypes = 
                        "BusinessLayer.*"
                         )]

          
                        namespace
                         BusinessLayer
          {
            [Configurable( AttributeExclude = 
                        true
                         )]
            
                        public
                         
                        static
                         
                        class
                         Helpers
            {

            }
          }