.Net Tips — Restricting attribute usage to specific type

Before you get your hopes up, sor­ry, there’s no built-in sup­port to allow you to restrict the usage of your attribute to a type eas­i­ly. That’s not to say that it’s impos­si­ble though!

If you’re read­ing this, I assume that you’ve tried to write a cus­tom attribute your­self and realised that the options avail­able in the Attrib­ut­e­Tar­gets enum is fair­ly lim­it­ed.

For those of you who are famil­iar with Post­Sharp, you would prob­a­bly have noticed that one of the meth­ods you can over­ride is curi­ous­ly called Com­pile­TimeVal­i­date and returns a boolean val­ue. You can use the Method­Base argu­ment passed into this method to inspect the type the attribute has been applied to and per­form some val­i­da­tion log­ic there, e.g.:

[Serializable]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method)]
public class MyAttribute : OnMethodBoundaryAspect
{
    public override bool CompileTimeValidate(System.Reflection.MethodBase method)
    {
        var type = method.DeclaringType;

        // if the attribute is used outside the Service class, fail the build
        if (!type.Equals(typeof(Service)))
        {
            throw new Exception("MyAttribute can only be used in the Service class");
        }

        return true;
    }
}

Because you have the meta­da­ta for the method in hand, there’s noth­ing to stop you from restrict­ing the usage of your attribute by input and return types too, e.g. attribute can only be applied to a method which returns string, etc.

If this is too much work for your lik­ing, Marc Grav­ell blogged about a neat trick you could do to achieve the same effect here. The trick exploits the pro­tect­ed mod­i­fi­er in C# so that an attribute declared in the base type can be used by derived types but not by oth­ers. Here’s the code snip­pet from his orig­i­nal post:

abstract class MyBase
{
    [AttributeUsage(AttributeTargets.Property)]
    protected sealed class SpecialAttribute : Attribute {}
}

class ShouldBeValid : MyBase
{
    [Special] // works fine
    public int Foo { get; set; }
}

class ShouldBeInvalid
{
    // not a subclass of MyBase
    [Special] // type or namespace not found
    [MyBase.Special] // inaccessible due to protection level
    public int Bar{ get; set; }
}

Update: I saw anoth­er relat­ed ques­tion on Stack­Over­flow here, as far as I’m aware you won’t be able to do this with­out the help of Post­Sharp as you won’t have access to the access mod­i­fiers (pub­lic, pro­tect­ed, inter­nal or pri­vate) that was applied to the field when it was declared. With Post­Sharp how­ev­er, this is how you would could go about doing such val­i­da­tion at com­pile time and make sure the attribute is applied to a pub­lic or pro­tect­ed field:

[Serializable]
[AttributeUsage(AttributeTargets.Field)]
public class MyAttribute : OnFieldAccessAspect
{
    public override bool CompileTimeValidate(System.Reflection.FieldInfo field)
    {
        if (field.IsPublic || field.IsFamily)
        {
            throw new Exception("Attribute can only be applied to Public or Protected fields");
        }

        return true;
    }
}