Sean Carpenter

GetCustomAttributes() and Overridden Properties

When using custom attributes in .Net, one of the most common tasks is to query a member for any custom attributes that are applied. The MemberInfo class has a GetCustomAttributes method for just this purpose.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ExampleAttribute : Attribute
{
}

public class ClassWithAttribute
{
    [Example]
    public virtual string Name { get; set; }
    [Example]
    public virtual int Age { get; set; }
}

public static class AttributeChecker
{
    public static IEnumerable<string> PropertiesWithAttribute(Type type)
    {
        IList<string> propertiesWithAttribute = new List<string>();
        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach(var property in properties)
        {
            var attributes = property.GetCustomAttributes(typeof(ExampleAttribute), true);
            if (attributes.Length > 0) { propertiesWithAttribute.Add(property.Name); }
        }
        return propertiesWithAttribute;
    }
}

// Somewhere where the presence of the attribute is being checked
var properties = AttributeChecker.PropertiesWithAttribute(typeof(ClassWithAttribute));

This is fairly straightforward reflection code that loops through all of the public properties on ClassWithAttribute and returns the names of any that have the ExampleAttribute applied. In the example shown, on line 29 the properties variable will be a list containing the words “Name” and “Age”.

But what happens if we use a class derived from ClassWithAttribute?

1
2
3
4
5
6
7
8
public class DerivedClassWithAttribute : ClassWithAttribute {
    // Override the "Name" property to supply an additional attribute.
    [DefaultValue("Phil")]
    public override string Name { get; set;}
}

var properties = AttributeChecker.PropertiesWithAttribute(typeof(DerivedClassWithAttribute));
// What does properties contain?

Perhaps surprisingly, the properties variable on line 7 contains the single word “Age”. This surprised me since I’m supplying true as the second parameter to GetCustomAttributes() on line 21 of the first example. When the second parameter is true, the member’s inheritance chain should be searched for attributes. So why didn’t it find the ExampleAttribute declared in ClassWithAttribute? The answer is in the documentation, although I missed it the first few times I read it (emphasis mine):

inherit: true to search this member’s inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.

The second parameter to GetCustomAttributes() is ignored if the method is being called on a property or an event (we have a property in this case). Luckily the “Remarks” section of the documentation has a solution (emphasis mine):

This method ignores the inherit parameter for properties and events. To search the inheritance chain for attributes on properties and events, use the appropriate overloads of the Attribute.GetCustomAttributes method.

So in this case we can simply replace line 21 in AttributeChecker.PropertiesWithAttribute() with the following:

1
var attributes = Attribute.GetCustomAttributes(property, typeof(ExampleAttribute), true);

Now running the code for DerivedClassWithAttribute will correctly return both “Name” and “Age”. A simple change, but something that tripped me up for a while none the less.