14

I have a custom attribute which is applied to class properties and the class itself. Now all the classes that must apply my custom attribute are derived from a single base class.

How can I restrict my Custom Attribute so that it can be applied to only those classes, that must derive from my base class? How do I do this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
this. __curious_geek
  • 42,787
  • 22
  • 113
  • 137

2 Answers2

20

Darn, I hate it when I prove myself wrong... it works if you define the attribute as a protected nested type of the base-class:

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

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

(original answer)

You cannot do this - at least, not at compile time.

Attributes can be restricted (for exmaple) to "class", "struct", "method", "field", etc - but nothing more granular.

Of course, since attributes do nothing by themselves (ignoring PostSharp), they will be inert on other types (and inert on your types unless you add some code to look at the attributes at runtime).

You could write an FxCop rule, but that seems overkill.

I wonder, however, whether an attribute is the best choice:

Now all the classes that must apply my custom attribute are derived from a single base class.

If they must apply your custom attribute, maybe an abstract (or maybe just virtual) property / method would be a better choice?

abstract class MyBase {
    protected abstract string GetSomeMagicValue {get;}
}
class MyActualType : MyBase {
    protected override string GetSomeMagicValue {get {return "Foo";}}
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • thanks marc. I'm applying attributes to the properties for mapping. The base class has a method returns all the properties in derived class that has this attribute applied on to it. Moreover there's no meaning allowing an attribute to be applied to any type or member without and significant purpose. – this. __curious_geek Jun 20 '09 at 08:44
  • Even I didnt find any documentation for doing this so before I reached to any conclusion I wanted to give it a chance with stackoverflow. I believe that attributes are applied purposefully, so why allow it to be applied to those type or members that do not carry the purpose. – this. __curious_geek Jun 20 '09 at 08:47
  • "So why allow it" - simply, you can't prevent it at the moment. There is no mechanism to disallow it. – Marc Gravell Jun 20 '09 at 08:58
  • I found this interesting enough to blog about it: http://marcgravell.blogspot.com/2009/06/restricting-attribute-usage.html – Marc Gravell Jun 20 '09 at 09:17
  • @Marc: Thanks marc, you rock. I came across this need while I was designing and implementing my own compact ORM. I was almost done with my work and was looking forward to restrict the attribute usage to classes only derived from my EntityBase class. – this. __curious_geek Jun 20 '09 at 13:46
  • I know this is old but I recently (in attempts to do this exact thing) noticed something as I was coding: The `AttributeUsageAttribute` via the `Attribute` type DOES do this somehow. If you decorate your class with an `AttributeUsage` attribute, but don't derive from the base `Attribute` class, you will get compiler error [CS0641](https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs0641). I don't see anything in the reference source that would cause this either but would love to know how they do it.. – xDaevax Jul 22 '20 at 12:31
0

I'm not aware of any way to do this with custom attributes.
A better way would be to get the classes to implement an interface with a generic constraint

so

class MyAttribute
{
     // put whatever custom data you want in here
}

interface IAttributeService<T> where T : MyBaseClass
{
     MyAttribute Value{get;}
}

then your base class would do this

class MyBaseClass : IAttributeService<MyBaseClass>
{
     private MyAttribute m_attrib;

     IAttributeService<MyClass>.Value
     {
         get {return m_attrib;}
     }
}

That way the compiler will enforce the constraint at compile time so that only classes
derived from MyBaseClass can implement the interface.
I made it fairly extensible by defining a MyAttribute class but you could of course just return a string or a bool if you needed something simpler

I also made the MyAttribute member private so that derived classes couldn't see it and change it but could make it protected and allow derived classes access if you wanted to.

zebrabox
  • 5,694
  • 1
  • 28
  • 32