I found the documentation for AttribuetUsageAttribute to be very ambiguous, particularly regarding the Inherited property. Here’s a quick test on the behavior of the AttributeUsage.Inherited flag.  This affects how the attribute is queried via GetCustomerAttributes(). Here’s how …

There are 4 pivots:

1. In the usage case, is there an attribute on the derived class of the same type as the base class?

    [Test(Value="Base")]
    class Base
    {
    }

    [Test(Value = "Derived")]
    class Derived : Base
    {
    }

 

2. On AttributeUsage, setting AllowMultiple

3. On AttributeUsage, setting Inherited

    [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
    class TestAttribute : Attribute
    {
        public string Value { get; set; }

        public override string ToString()
        {
            return Value;
        }
    }

 

4. In the query case, is inherit true or false?

    var attrs = typeof(Derived).GetCustomAttributes(inherit: true);

 

Outputs for each combination

That’s 2^4 = 16 cases.  Here are the outputs for the following code snippet. Blank cells means false. Results is

            foreach (var attr in attrs)
            {
                Console.WriteLine(attr);
            }

[1] Attr on Derived class?

[2] AttributeUsage AllowMultiple=?

[3] AttributeUsage Inherited=?

[4]GetCustomAttributes

Inherit=

Console output:

1

Yes

True

True

True

Derived

Base

2

Yes

True

True

 

Derived

3

Yes

True

 

True

Derived

4

Yes

True

   

Derived

5

Yes

 

True

True

Derived

6

Yes

 

True

 

Derived

7

Yes

   

True

Derived

8

Yes

     

Derived

9

No

True

True

True

Base

10

No

True

True

 

(none)

11

No

True

 

True

(none)

12

No

True

   

(none)

13

No

 

True

True

Base

14

No

 

True

 

(none)

15

No

   

True

(none)

16

No

     

(none)

 

Other comments:

I found case #1 vs. case #5 to be particularly interesting.

Of course, you can always manually walk the inheritance chain. This has the added perk of telling you at which level the attribute occurs :

            for(var t = typeof(Derived); t != null; t = t.BaseType)            
            {
                foreach(var attr in t.GetCustomAttributes(inherit:false))
                {
                    Console.WriteLine("type={0},attr={1}", t.Name, attr);
                }
            }

 

Assuming the derived class has an attr, this will print:

type=Derived,attr=Derived
type=Base,attr=Base
type=Object,attr=System.Runtime.InteropServices.ComVisibleAttribute
type=Object,attr=System.Runtime.InteropServices.ClassInterfaceAttribute
type=Object,attr=__DynamicallyInvokableAttribute
type=Object,attr=System.SerializableAttribute

Note the attributes on System.Object. These have inherited=false, so don’t show up until we explicitly query.