| Severity | Error |
| Reported by | PostSharp.Patterns.Diagnostics |
| Message | Cannot apply the [Log] aspect to method '<Method>' because it is abstract. |
Cause
The LogAttribute aspect, and any other aspect derived from LogAttributeBase, logs the execution of a method by adding code to its body. An abstract method, including a method of an interface, has no body, so PostSharp reports DIA002 instead of logging it.
When you add the aspect with an attribute, PostSharp skips abstract methods: the aspect excludes them from multicasting, and an attribute written directly on an abstract method is reported as PS0091. DIA002 therefore comes from code that adds the aspect programmatically, typically an aspect that implements IAspectProvider and returns an AspectInstance for every method of a type without checking whether it is abstract.
In the following example, the [LogAllMethods] aspect adds the [Log] aspect to every public method of PaymentProcessor, including the abstract Charge method. PostSharp reports the error on the abstract method.
using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Patterns.Diagnostics;
using PostSharp.Patterns.Diagnostics.Backends.Console;
using PostSharp.Serialization;
namespace Errors.DIA002_Error;
// Adds the [Log] aspect to the methods of the target class and of the classes derived from it.
[PSerializable]
[MulticastAttributeUsage( MulticastTargets.Class, Inheritance = MulticastInheritance.Strict )]
public class LogAllMethodsAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
Type type = (Type) targetElement;
foreach ( MethodInfo method in type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
{
yield return new AspectInstance( method, new LogAttribute() );
}
}
}
[LogAllMethods]
public abstract class PaymentProcessor
{
// The aspect provider adds the [Log] aspect to this abstract method too.
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}." );
}
public static class Program
{
public static void Main()
{
LoggingServices.DefaultBackend = new ConsoleLoggingBackend();
PaymentProcessor processor = new CardPaymentProcessor();
processor.Charge( 25 );
processor.Refund( 10 );
}
}
error DIA002: DIA002_Error.cs(32,26): Cannot apply the [Log] aspect to method 'Errors.DIA002_Error.PaymentProcessor.Charge(System.Decimal)' because it is abstract.
Resolution
Skip abstract methods in the code that adds the aspect, for example by testing IsAbstract in the ProvideAspects method of the aspect provider. Log the methods that override the abstract method instead: they have a body.
In the following example, the aspect provider skips abstract methods. Because [LogAllMethods] is inherited by derived classes, the aspect logs CardPaymentProcessor.Charge, which overrides the abstract method.
using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Patterns.Diagnostics;
using PostSharp.Patterns.Diagnostics.Backends.Console;
using PostSharp.Serialization;
namespace Errors.DIA002_Fixed;
// Adds the [Log] aspect to the methods of the target class and of the classes derived from it.
[PSerializable]
[MulticastAttributeUsage( MulticastTargets.Class, Inheritance = MulticastInheritance.Strict )]
public class LogAllMethodsAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
Type type = (Type) targetElement;
foreach ( MethodInfo method in type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
{
// Abstract methods are skipped: their overrides in derived classes are logged instead.
if ( !method.IsAbstract )
{
yield return new AspectInstance( method, new LogAttribute() );
}
}
}
}
[LogAllMethods]
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}." );
}
public static class Program
{
public static void Main()
{
LoggingServices.DefaultBackend = new ConsoleLoggingBackend();
PaymentProcessor processor = new CardPaymentProcessor();
processor.Charge( 25 );
processor.Refund( 10 );
}
}
Debug | Trace | CardPaymentProcessor.Charge({25}) | Starting.
Charged 25.
Debug | Trace | CardPaymentProcessor.Charge({25}) | Succeeded.
Debug | Trace | PaymentProcessor.Refund({10}) | Starting.
Refunded 10.
Debug | Trace | PaymentProcessor.Refund({10}) | Succeeded.
See Also
Reference
LogAttribute
LogAttributeBase
IAspectProvider
Other Resources
Getting Started with PostSharp Logging
Adding Aspects Programmatically Using IAspectProvider