Open sandboxFocusImprove this doc

PS0103: MulticastAttributeUsage Inheritance Specified Twice

Severity Error
Reported by PostSharp
Message On type "<Type>", cannot override the <Property> property of [MulticastAttributeUsage] because a base type already specifies it.

Cause

An attribute class inherits the MulticastAttributeUsageAttribute of its base classes. PostSharp combines the usages from the root base class down to the attribute class: most properties can only be restricted by a derived class (see PS0072), but the Inheritance property, which says whether the attribute is inherited by derived classes and overriding members (see Understanding Aspect Inheritance), can be set only once in the class hierarchy. PostSharp reports PS0103 when a class sets Inheritance in its [MulticastAttributeUsage] although a base class already sets it, even to the same value.

<Type> is the class whose [MulticastAttributeUsage] sets the property again, and <Property> is Inheritance. Because PostSharp cannot know where the attribute applies, it also stops with PS0055.

In the following example, the base class AuditAspect makes all the auditing aspects inherited, and TraceAttribute tries to make itself not inherited.

using System;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Serialization;

namespace Errors.PS0103_Error;

// The base class of the auditing aspects makes all of them inherited by overriding methods.
[PSerializable]
[MulticastAttributeUsage( MulticastTargets.Method, Inheritance = MulticastInheritance.Strict )]
public abstract class AuditAspect : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"{this.GetType().Name.Replace( "Attribute", "" )}: {args.Method.DeclaringType!.Name}.{args.Method.Name}" );
    }
}

[PSerializable]
public sealed class AuditAttribute : AuditAspect
{
}

// This aspect must not be inherited, but its base class already specifies the inheritance.
[PSerializable]
[MulticastAttributeUsage( Inheritance = MulticastInheritance.None )]
public sealed class TraceAttribute : AuditAspect
{
}

public class PaymentProcessor
{
    [Audit]
    public virtual void Process( decimal amount ) => Console.WriteLine( $"Processing {amount:0.00}." );

    [Trace]
    public void Validate( decimal amount ) => Console.WriteLine( $"Validating {amount:0.00}." );
}

public class CardPaymentProcessor : PaymentProcessor
{
    public override void Process( decimal amount ) => Console.WriteLine( $"Charging {amount:0.00} to the card." );
}

public static class Program
{
    public static void Main()
    {
        CardPaymentProcessor processor = new();
        processor.Validate( 25m );
        processor.Process( 25m );
    }
}
error PS0103: PS0103_Error.cs(27,21): On type "Errors.PS0103_Error.TraceAttribute", cannot override the Inheritance property of [MulticastAttributeUsage] because a base type already specifies it.
error PS0055: The processing of multicast attributes cannot continue because of the previous errors.

Resolution

  • When the aspect classes derived from a base class need different inheritance modes, remove the Inheritance property from the [MulticastAttributeUsage] of the base class, and set it in each derived class.
  • When the inheritance mode of the base class suits the derived class, remove the Inheritance property from the [MulticastAttributeUsage] of the derived class.
  • To change the inheritance of a single usage of the aspect, set the AttributeInheritance property where you apply the aspect, instead of changing the class.

In the following example, the base class leaves the inheritance to each aspect: AuditAttribute is inherited by the override of Process, and TraceAttribute is not inherited.

using System;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Serialization;

namespace Errors.PS0103_Fixed;

// The base class of the auditing aspects leaves the inheritance to each aspect.
[PSerializable]
[MulticastAttributeUsage( MulticastTargets.Method )]
public abstract class AuditAspect : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"{this.GetType().Name.Replace( "Attribute", "" )}: {args.Method.DeclaringType!.Name}.{args.Method.Name}" );
    }
}

[PSerializable]
[MulticastAttributeUsage( Inheritance = MulticastInheritance.Strict )]
public sealed class AuditAttribute : AuditAspect
{
}

// Each aspect specifies its own inheritance, once in the class hierarchy.
[PSerializable]
[MulticastAttributeUsage( Inheritance = MulticastInheritance.None )]
public sealed class TraceAttribute : AuditAspect
{
}

public class PaymentProcessor
{
    [Audit]
    public virtual void Process( decimal amount ) => Console.WriteLine( $"Processing {amount:0.00}." );

    [Trace]
    public void Validate( decimal amount ) => Console.WriteLine( $"Validating {amount:0.00}." );
}

public class CardPaymentProcessor : PaymentProcessor
{
    public override void Process( decimal amount ) => Console.WriteLine( $"Charging {amount:0.00} to the card." );
}

public static class Program
{
    public static void Main()
    {
        CardPaymentProcessor processor = new();
        processor.Validate( 25m );
        processor.Process( 25m );
    }
}
Trace: PaymentProcessor.Validate
Validating 25.00.
Audit: CardPaymentProcessor.Process
Charging 25.00 to the card.

See Also

Reference

MulticastAttributeUsageAttribute
MulticastInheritance

Other Resources

Understanding Aspect Inheritance
Understanding Attribute Multicasting