AttributeUsage.Inherited flag
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.
Comments
- Anonymous
July 18, 2013
Enjoyed this post. Very interesting.. definitely important to understand this in a world with so many decorated classes.