C# AllowMultiple Attributes on Derived Classes

Viewed 261

Consider the code snippet below:

[AttributeUsage(AttributeTargets.Class, AllowMultiple =true)]
public class FooAttribute : Attribute {
    public string Bar { get; set; } 
}

[Foo(Bar = "A")]
public class A{}

[Foo(Bar = "B")]
public class B : A{}

[Foo(Bar = "C")]
public class C : B{}

If I tried to get the FooAttribute of class C

var attrList = typeof(C).GetCustomAttributes(typeof(FooAttribute), true) as FooAttribute[];

I will get all 3 attributes. And the order is C, B, A. So here are my questions:

  • Is this order a guaranteed behavior, which means the attribute of a derived class always appears at a smaller index than its base class?
  • Is it possible to know which specific type the attribute is attached to?
1 Answers

Generally the order is not guaranteed and the compiler will yield different results depending on the platform, culture and compiler version. However in this specific scenario it is guaranteed to be in the correct order (at least partly). The exception is when you are assigning multiple attributes at the same inheritance level:

[AttributeUsage(AttributeTargets.Class, AllowMultiple =true)]
public class FooAttribute : Attribute {
    public string Bar { get; set; } 
}

[Foo(Bar = "A")]
public class A{}

[Foo(Bar = "B"), Foo(Bar = "C")]
public class B : A{}

The order will always be the same for A (last) but B and C can vary: C-B-A or B-C-A. In the following example the order is not guaranteed at all:

[Foo(Bar = "A"), Foo(Bar = "B"), Foo(Bar = "C")]
public class A{}

But the order will remain if you assign one attribute to every level of inheritance. This is due to the implementation in .NET (Framework) as it is traversing each base type to collect all attributes (if you Type.GetCustomAttributes(Type, inherit: true)). This can be seen in this section of code (System.Private.CoreLib, System.Reflection):

private static Attribute[] InternalGetCustomAttributes(EventInfo element, Type type, bool inherit)
{
    [...]

    // walk up the hierarchy chain
    Attribute[] attributes = (Attribute[])element.GetCustomAttributes(type, inherit);
    if (inherit)
    {
        // create the hashtable that keeps track of inherited types
        Dictionary<Type, AttributeUsageAttribute> types = new Dictionary<Type, AttributeUsageAttribute>(11);
        // create an array list to collect all the requested attibutes
        List<Attribute> attributeList = new List<Attribute>();
        CopyToArrayList(attributeList, attributes, types);

        EventInfo? baseEvent = GetParentDefinition(element);
        while (baseEvent != null)
        {
            attributes = GetCustomAttributes(baseEvent, type, false);
            AddAttributesToList(attributeList, attributes, types);
            baseEvent = GetParentDefinition(baseEvent);
        }
        Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count);
        attributeList.CopyTo(array, 0);
        return array;
    }
    else
        return attributes;
}

Note that this is for events but this also true for the MemberInfo (Type).

Related