Open sandboxFocusImprove this doc

LA0126: Aspect Applied to an Abstract Method

Severity Error
Reported by PostSharp
Message Aspect "<Aspect>" cannot be applied to "<Target>" because the method is abstract.

Cause

Some aspects work by adding code to the body of the target method. An abstract method has no body, so PostSharp reports LA0126 and skips the aspect when:

<Target> is the method, or the parameter. For an abstract method of an interface, PostSharp reports LA0130 instead.

The usage of these two aspect classes excludes abstract methods, so when you apply them as custom attributes, multicasting skips the abstract methods. An aspect that an IAspectProvider provides is not filtered this way: its target is the declaration passed to the AspectInstance constructor. PostSharp then reports the message on the declaration that the provider is applied to.

In the following example, the [LogMethods] aspect provider passes every method of PaymentProcessor to the [Log] aspect, including the abstract Charge method.

using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0126_Error;

[PSerializable]
public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering {args.Method.Name}." );
    }
}

[PSerializable]
public class LogMethodsAttribute : TypeLevelAspect, IAspectProvider
{
    public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
    {
        var type = (Type) targetElement;

        foreach ( var method in type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
        {
            yield return new AspectInstance( method, new LogAttribute() );
        }
    }
}

// The provider passes the abstract Charge method to the aspect.
[LogMethods]
public abstract class PaymentProcessor
{
    public abstract void Charge( decimal amount );

    public void Refund( decimal amount ) => Console.WriteLine( $"Refunded {amount}." );
}

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

public static class Program
{
    public static void Main()
    {
        PaymentProcessor processor = new CardPaymentProcessor();
        processor.Charge( 100 );
        processor.Refund( 25 );
    }
}
error LA0126: LA0126_Error.cs(34,23): Aspect "Errors.LA0126_Error.LogAttribute" cannot be applied to "Errors.LA0126_Error.PaymentProcessor.Charge(System.Decimal)" because the method is abstract.

Resolution

  • In the ProvideAspects method, skip the methods whose IsAbstract property is true. Apply the aspect to the methods that override them instead.
  • When you apply a location validation aspect to a parameter of an abstract method, apply it to the parameter of each overriding method instead.

In the following example, the provider skips the abstract method. The Refund method is logged; Charge in CardPaymentProcessor is not, because the provider is not applied to that class.

using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0126_Fixed;

[PSerializable]
public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering {args.Method.Name}." );
    }
}

[PSerializable]
public class LogMethodsAttribute : TypeLevelAspect, IAspectProvider
{
    public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
    {
        var type = (Type) targetElement;

        foreach ( var method in type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
        {
            // Abstract methods have no body to enhance, so they are skipped.
            if ( !method.IsAbstract )
            {
                yield return new AspectInstance( method, new LogAttribute() );
            }
        }
    }
}

[LogMethods]
public abstract class PaymentProcessor
{
    public abstract void Charge( decimal amount );

    public void Refund( decimal amount ) => Console.WriteLine( $"Refunded {amount}." );
}

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

public static class Program
{
    public static void Main()
    {
        PaymentProcessor processor = new CardPaymentProcessor();
        processor.Charge( 100 );
        processor.Refund( 25 );
    }
}
Charged 100 to the card.
Entering Refund.
Refunded 25.

See Also

Reference

IAspectProvider
OnMethodBoundaryAspect
ILocationValidationAspect<T>

Other Resources

Adding Aspects Dynamically
Adding Aspects Programmatically Using IAspectProvider