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).