考虑下面的代码片段:
[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{}
如果我尝试获取 C
类的 FooAttribute
var attrList = typeof(C).GetCustomAttributes(typeof(FooAttribute), true) as FooAttribute[];
我将获得所有 3 个属性。顺序是C, B, A。所以这是我的问题:
- 此顺序是否是一种有保证的行为,这意味着派生类的属性总是出现在比其基类更小的索引处?
- 是否可以知道属性附加到哪个特定类型?
最佳答案
通常无法保证顺序,编译器会根据平台、文化和编译器版本产生不同的结果。但是,在这种特定情况中,可以保证顺序正确(至少部分正确)。异常(exception)是当您在同一继承级别分配多个属性时:
[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{}
A 的顺序始终相同(最后),但 B 和 C 可以不同:C-B-A 或 B-C-A。在以下示例中,根本无法保证顺序:
[Foo(Bar = "A"), Foo(Bar = "B"), Foo(Bar = "C")]
public class A{}
但是如果您将一个属性分配给每个继承级别,顺序将保持不变。这是由于 .NET (Framework) 中的实现,因为它遍历每个基本类型以收集所有属性(如果您 Type.GetCustomAttributes(Type, inherit: true)
)。这个可以看这段代码(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;
}
请注意,这适用于 events
,但对于 MemberInfo
(Type
)也是如此。
关于C# 允许派生类的多个属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68372586/