Question

Is it possible to have a Postsharp attribute that can be applied to a class, interface, property, field or method without the use of MulticastAttributeUsage so that the user decides exactly which members it is applied against without mulitcasting onto every member.

If I use MulticastAttributeUsage and use my attribute on a class it automatically puts a new instance of my attribute on all the properties, methods, etc. in the generated IL.

If I don't use it, it won't compile because it complains that I need to use it (I'm deriving from the Aspect class).

Here's my scenario:

I am creating a new Serialisation attribute, so I want to be able to do this:

[Serialise(ApplyToProperty="Test2", Name="Blah2")]
public class MyClass {

    [Serialise(Name="Blah1")]
    public string Test1 { get; set; }

    public bool Test2 { get; set; }
}

When I reflect at runtime with using the MulticastAttributeUsage I'm getting two Serialise attributes on Test1. When I look at the IL generated it shows this:

[Serialise(ApplyToProperty = "Test2", Name = "Blah2")]
public class Test {

    [Serialise(ApplyToProperty = "Test2", Name = "Blah2"), Serialise(Name = "Blah")]
    public string Test1 { get; set; }

    [Serialise(ApplyToProperty = "Test2", Name = "Blah2")]
    public bool Test2 { get; set; }

}

I could just not use Postsharp and reflection at runtime would have exactly what I need, but I need to use Postsharp to introduce another aspect. My SerialiseAttribute code is this:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Field)]
[MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Interface | MulticastTargets.Property | MulticastTargets.Field, AllowMultiple = true, PersistMetaData = true, Inheritance = MulticastInheritance.None)]
public class SerialiseAttribute : Aspect, IAspectProvider {

    public string ApplyToProperty { get; set; }
    public string Name { get; set; }

    public override bool CompileTimeValidate(object target) {           
        return false;
    }       

    IEnumerable<AspectInstance> IAspectProvider.ProvideAspects(object targetElement) {
        // Code that introduces a different attribute
    }

}

Notice also that the fact that I am returning false from CompileTimeValidate does not seem to have any effect on the IL generated, it seems the "silent failure" does not mean "do not apply the attribute to it" unfortunately. So how can I prevent Postsharp actually multicasting the attribute to all members?

Was it helpful?

Solution

Your custom attribute derives from Aspect class, which in turn is derived from MulticastAttribute - this is why you're getting the multicasting behavior.

What you can do instead is to derive from System.Attribute and implement all the interfaces you need. That would be IAspectProvider and IValidableAnnotation in your case.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Field)]
public class Serialise : Attribute, IAspectProvider, IValidableAnnotation
{
    public string ApplyToProperty { get; set; }
    public string Name { get; set; }

    public bool CompileTimeValidate(object target)
    {
        return false;
    }

    public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
    {
        // Code that introduces a different attribute
    }
}

As to the CompileTimeValidate method - this method will run only after the multicasting has been performed to determine whether to introduce aspects for each given code element with attribute already applied.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top