Search code examples
c#attributesaoppostsharp

Native PostSharp attributes are ignored if injected using Custom Attributes


Consider the following code:

 [AttributeUsage(validOn: AttributeTargets.Property)]
public sealed class ExcludeAttribute : Attribute
{
}

[PSerializable]
public sealed class PsDependencyPropertyAttribute : TypeLevelAspect, IAspectProvider
{
    public PsDependencyPropertyAttribute()
    {
    }

    public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
    {
        var targetType = (Type)targetElement;

        //---

        var introduceObfuscationAspect =
            new CustomAttributeIntroductionAspect(
                new ObjectConstruction(constructor: typeof(ObfuscationAttribute).GetConstructor(types: Type.EmptyTypes)));

        introduceObfuscationAspect.CustomAttribute.NamedArguments.Add(key: "Feature",               value: "renaming");
        introduceObfuscationAspect.CustomAttribute.NamedArguments.Add(key: "Exclude",               value: true);
        introduceObfuscationAspect.CustomAttribute.NamedArguments.Add(key: "StripAfterObfuscation", value: true);

        // add a Obfuscation attribute to every relevant property
        foreach (var property
            in targetType.GetProperties(
                    bindingAttr: BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Instance)
                .Where(
                    predicate: property =>
                        property.CanWrite &&
                        !property.IsDefined(attributeType: typeof(ExcludeAttribute), inherit: false)))

            yield return new AspectInstance(targetElement: property, aspect: introduceObfuscationAspect);

        //---

        //! NATIVE PostSharp ATTRIBUTES DON'T GET PROCESSED IF THEY'RE INJECTED AT COMPILE TIME

        var introduceDependencyPropertyAspect =
            new CustomAttributeIntroductionAspect(
                new ObjectConstruction(constructor: typeof(DependencyPropertyAttribute).GetConstructor(types: Type.EmptyTypes)));

        // add a DependencyPropertyA attribute to every relevant property
        foreach (var property
            in targetType.GetProperties(
                    bindingAttr: BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance)
                .Where(
                    predicate: property =>
                        property.CanWrite &&
                        !property.IsDefined(attributeType: typeof(ExcludeAttribute), inherit: false)))

            yield return new AspectInstance(targetElement: property, aspect: introduceDependencyPropertyAspect);
    }
}

Using this custom attribute does indeed inject the 2 attributes [DependencyProperty, Obfuscation(Feature = "renaming", Exclude = true, StripAfterObfuscation = true)], the only problem is that DependencyProperty is a PostSharp attribute and doesn't get processed, just injected. And that's reasonable because the custom attribute tells PS just to inject those 2 attributes, BUT is there a way to have the PS DependencyProperty processed when it's injected using a custom attribute?


Solution

  • You can provide DependencyPropertyAttribute directly as an aspect to the target property instead of going via CustomAttributeIntroductionAspect. For example:

    yield return new AspectInstance(targetElement: property, aspect: new DependencyPropertyAttribute());
    

    This is why DependencyPropertyAttribute is not processed when introduced as an attribute:

    PostSharp pipeline processes the assembly in several phases. The processing of the attributes is executed first, after that the aspect weavers are executed. If any aspect is emitting a new custom attribute during this phase, the attribute will not be processed by PostSharp anymore because the attribute processing phase has already completed.