5

I'm working with custom attributes, which again have a common base class. The AttributeUsage attribute is declared on the base attribute class, not on the concrete attribute classes. My attribute usage on the base class says AttributeTargets.Class, AllowMultiple = true, Inherited = true

Now when i'm working with my concrete attribute classes they use the AttributeUsage settigs from the base attribute class for Target and AllowMultiple, but not for Inherited -> this does not make any sense for me.

Here's a little code example which does not output what i expect:

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public abstract class TestAttributeBase : Attribute
    {
    }
    
    public class TestAttribute : TestAttributeBase
    {
        public string Value {get; private set;}
        
        public TestAttribute(string value)
        {
            Value = Value;  
        }
    }
    
    [TestAttribute("A")]
    public class A {
    }
    
    [TestAttribute("B")]
    [TestAttribute("C")]
    public class B : A
    {
    }
    
    public static void Main()
    {
        var customAttributes = typeof(B).GetCustomAttributes<TestAttributeBase>(true).ToList();
        Console.WriteLine(customAttributes.Count);
    }

Here's the dotnetfiddle Link with the same code: https://dotnetfiddle.net/BZ7R4S

If you run this code it outputs "2", but i would expect "3". The [AttributeUsage] of TestAttributeBase is definetly inherited to the TestAttribute, because if you change AttributeTargets.Class to e.g. AttributeTargets.Field, the code does not compile. If you set AllowMultiple to false the code does not compile -> I discern from this that [AttributeUsage] gets inherited to the concrete Attribute classes, but not the Inherited property.

Does this make any sense?

If i add the exact same [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] to the concrete TestAttribute, it works and ouputs 3

antifish
  • 51
  • 1
  • That is interesting indeed. According to the documentation, attributes are inheritable by default, but... – Nick Jul 06 '23 at 13:34
  • 1
    "Inheritance" is a bit ambiguous. Here it means "look in the base class only when the attribute is not found". It does work similar for normal methods, you only call the base class method if the derived class doesn't have a method with the same name. – Hans Passant Jul 06 '23 at 13:48
  • @HansPassant i understand the concept behind your idea / explniation, but that does not explain why it works if i define AttributeUsage directly on the TestAttribute Class? If "B.GetCustomAttributes(true)" would only search base/upper class if the attribute was not found on the orignal class it would never return Attribute from A - which it does if add AttributeUsage(Inherit = true) on "TestAttribute" – antifish Jul 06 '23 at 14:18
  • Found very old [discussion @github](https://github.com/dotnet/runtime/discussions/53016) which has not received any answers. Created an issue from it to raise the attention. – Guru Stron Jul 06 '23 at 16:02

1 Answers1

0

As far as I know, 'Inherited' controls whether attributes are inherited to 'subclasses'. What I could notice is that if you make 'Inherited = true' in the base class 'TestAttributeBase', the derived attributes inherit this configuration and can be applied in the subclasses, however if you get the attributes using GetCustomAttributes>TestAttributeBase<, the Inherited property does not is taken into account. What you can do is ensure that derived attributes also have the 'Inherited = true' property by explicitly applying the AttributeUsage attribute on concrete attribute classes.

  • Thanks for the answer. Yes if i explicitly add the AttributeUsage to the concrete attribute class then it works (as stated in my original post). But i find it strange behaviour that AttributeUsage is inherited down to subclass-attributes, but not all properties of it... – antifish Jul 06 '23 at 13:40