Search code examples
c#genericspostsharp

Excluding an aspect from specific implementations of a generic class?


Say I have a generic class:

public abstract class MyClass<T> {
    // Contents
}

How can I specify an exclude for my aspect for only certain types of T? I am adding aspects in ithe AssemblyInfo.cs, like so:

[assembly: LogMethod(AttributePriority = 0,
                     AttributeTargetTypeAttributes = MulticastAttributes.Public,
                     AttributeTargetMemberAttributes = MulticastAttributes.Public,
                     AttributeTargetElements = MulticastTargets.Method)]

Solution

  • It is not possible to apply generic arguments filtering in a declarative way. For advanced filtering of the aspect targets you can override CompileTimeValidate method on your aspect and filter programmatically.

    However, even this is not going to be enough in the case you've described. Let's say you've applied the aspect to a method in MyClass<T>. At this point in compilation the T is not known yet, so it's not possible to perform the check. The concrete T is known when a field or variable of MyClass<T> is declared somewhere else in the code.

    The best option I can see in your case is to make the aspect instance-scoped and validate at run-time for each instance of the target class. You can find the sample implementation of this approach below.

    [PSerializable]
    public class LogMethodAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect
    {
        private bool disabled;
    
        public override void OnEntry( MethodExecutionArgs args )
        {
            if ( !this.disabled )
            {
                Console.WriteLine( "OnEntry: {0}({1})", args.Method.Name, args.Arguments.GetArgument( 0 ) );
            }
        }
    
        public object CreateInstance( AdviceArgs adviceArgs )
        {
            LogMethodAttribute clone = (LogMethodAttribute) this.MemberwiseClone();
    
            Type type = adviceArgs.Instance.GetType();
            if ( type.IsGenericType )
            {
                Type[] genericArguments = type.GetGenericArguments();
                // Filter out targets where T is string.
                if ( genericArguments[0] == typeof( string ) )
                {
                    clone.disabled = true;
                }
            }
    
            return clone;
        }
    
        public void RuntimeInitializeInstance()
        {
        }
    }
    
    class Program
    {
        static void Main( string[] args )
        {
            var obj1 = new Class1<int>();
            obj1.Method1(1);
    
            var obj2 = new Class1<string>();
            obj2.Method1("a");
        }
    }
    
    [LogMethod(AttributeTargetElements = MulticastTargets.Method)]
    public class Class1<T>
    {
        public void Method1(T a)
        {
        }
    }