Search code examples
postsharp

Why can I not apply IInstanceScopedAspect to static class in last version of PostSharp?


I have a such aspect:

[PSerializable]
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | AttributeTargets.Event, AllowMultiple = false )]
public abstract class LogAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect {

    // CompileTime/Init
    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo) {
    }
    // CompileTime/Validate
    public override bool CompileTimeValidate(MethodBase method) {
        return true;
    }

    // Runtime/Init/Static
    public override void RuntimeInitialize(MethodBase method) {
    }
    // Runtime/Init/Instance (Only if method is not static)
    object IInstanceScopedAspect.CreateInstance(AdviceArgs args) {
        var clone = (LogAttribute) MemberwiseClone();
        return clone;
    }
    void IInstanceScopedAspect.RuntimeInitializeInstance() {
    }


    // Advices
    public override void OnEntry(MethodExecutionArgs args) {
    }
    public override void OnSuccess(MethodExecutionArgs args) {
    }
    public override void OnException(MethodExecutionArgs args) {
    }
    public override void OnExit(MethodExecutionArgs args) {
    }
    public override void OnYield(MethodExecutionArgs args) {
    }
    public override void OnResume(MethodExecutionArgs args) {
    }

}

I used it for non-static and static methods and classes. But now I can not use IInstanceScopedAspect in static classes!

I'm getting error: Error LA0203 Cannot apply instance-level aspect "LogAttribute" to static class "...".

What was changed? How I can do it now?


Solution

  • This was disallowed in PostSharp 6.4 because it was possible to change the aspect's target class in a way that violated C#'s definition of a static classes (added instance members, etc.). The correct way now is to have two separate aspects for instance and static methods if you need to have IInstanceScopedAspect.

    The best way to achieve this is to have the IAspectProvider that creates a correct aspect:

    [PSerializable]
    public class LogAttribute : MethodLevelAspect, IAspectProvider
    {
        public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
        {
            if (targetElement is MethodBase method)
            {
                if (method.IsStatic)
                    yield return new AspectInstance(targetElement, new StaticLogAspect());
                else
                    yield return new AspectInstance(targetElement, new InstanceLogAspect());
            }
        }
    }
    
    [PSerializable]
    internal abstract class BaseLogAspect : IAspect
    {
        [OnMethodEntryAdvice]
        [SelfPointcut]
        public virtual void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Base");
        }
    }
    
    [PSerializable]
    internal class StaticLogAspect : BaseLogAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Static");
        }
    }
    
    [PSerializable]
    internal class InstanceLogAspect : BaseLogAspect, IInstanceScopedAspect
    {
        public object CreateInstance(AdviceArgs adviceArgs)
        {
            return new InstanceLogAspect();
        }
    
        public void RuntimeInitializeInstance()
        {
        }
    
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Instance");
        }
    }
    

    Note that IInstanceScopedAspect results in a one aspect object being allocated per instance of the target class. When you use this on methods such aspect may cause a quite significant increase of object's memory footprint.

    If you need instance-related information in your advice methods, it's better to have one "central" instance scoped aspect on the type. This aspect stores instance information and introduces an interface into the target class. The introduced interface is then used by method level aspects to obtain information about the instance.