自定义属性类是否继承“继承”属性用法标志
本文关键字:继承 属性 用法 标志 是否 自定义属性 | 更新日期: 2023-09-27 18:00:09
请考虑以下场景:
- 基属性类
BaseAttribute
有一个AttributeUsageAttribute
指定它不可继承 (Inherited = False
(。 - 派生属性类
DerivedAttribute
继承自该基属性类。 - 应用了派生属性的基域类
Base
。 - 要求从基域类继承
Derived
域类提供其自定义属性,包括继承的属性 (inherit: true
(。
这是相应的代码:
using System;
using System.Linq;
namespace ConsoleApplication26
{
class Program
{
static void Main ()
{
var attributes = typeof (Derived).GetCustomAttributes (true);
foreach (var attribute in attributes)
{
Console.WriteLine (
"{0}: Inherited = {1}",
attribute.GetType().Name,
attribute.GetType().GetCustomAttributes (typeof (AttributeUsageAttribute), true).Cast<AttributeUsageAttribute>().Single().Inherited);
}
}
}
[AttributeUsage (AttributeTargets.All, Inherited = false)]
public class BaseAttribute : Attribute
{
}
public class DerivedAttribute : BaseAttribute
{
}
[Derived]
public class Base
{
}
public class Derived : Base
{
}
}
在此方案中,GetCustomAttributes
API 返回 DerivedAttribute
类的实例。我本来希望它不会返回该实例,因为 http://msdn.microsoft.com/en-us/library/system.attributeusageattribute.aspx 说AttributeUsageAttribute
本身是可继承的。
现在,这是一个错误,还是在某个地方被预期/记录?
注(2013-02-20(: 实验表明,BaseAttribute
类的AttributeTargets
部分确实被DerivedAttribute
类继承。 例如,当我将BaseAttribute
上允许的目标更改为AttributeTargets.Method
时,C#编译器将不允许我将DerivedAttribute
应用于类。因此,Inherited = false
部分不被DerivedAttribute
继承是没有意义的,因此我倾向于在GetCustomAttributes
的实现中想到一个bug。
在编译时,构造类派生的元数据。编译器检查继承链以查看是否需要继承任何属性。在那里,它找到属性 DerivedAttribute。(我不知道是否考虑了 Object 上的属性,但它们被设置为无论如何都不会继承。若要查看它是否必须继承,它会检查发现的属性类上的 AttributeUsageAttribute。它没有自己的;语言规范指出:
未附加 AttributeUsage 属性的属性类 X,如
class X : Attribute { ... }
等效于以下内容:
[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false,
Inherited = true)
]
class X : Attribute { ... }
我的猜测是编译器以这种方式运行并创建元数据,该元数据指示类派生继承自定义属性 DerivedAttribute。
然后,在运行时,当获取类派生的自定义属性时,类不规则链从类派生到类基,其中找到派生属性。经过检查,可以看到类 DerivedAttribute 没有它自己的 AttributeUsageAttribute,但由于 AttributeUsageAttribute 可以继承,它从父级继承了一个。
在运行时,继承按预期执行,并且继承 BaseAttribute 的 AttributeUsageAttribute (已继承设置为 false(。
我拉长了继承链,以检查继承的属性是否是最近的父属性,而不是根的属性:
using System;
using System.Linq;
public class Program
{
public static void Main ()
{
var attributes = typeof (Level4Class).GetCustomAttributes (true);
foreach (var attribute in attributes)
{
Console.WriteLine (
"{0}: Inherited = {1}; ClassLevel = {2}",
attribute.GetType().Name,
attribute.GetType().GetCustomAttributes (typeof (AttributeUsageAttribute), true).Cast<AttributeUsageAttribute>().Single().Inherited,
((Level1Attribute)attribute).ClassLevel);
}
}
}
[AttributeUsage (AttributeTargets.Class, Inherited = false)]
public class Level1Attribute : Attribute
{
public int ClassLevel { get; set; }
}
public class Level2Attribute : Level1Attribute
{
}
public class Level3Attribute : Level2Attribute
{
}
[Level1(ClassLevel=1)]
[Level2(ClassLevel=1)]
[Level3(ClassLevel=1)]
public class Level1Class
{
}
[Level1(ClassLevel=2)]
[Level2(ClassLevel=2)]
[Level3(ClassLevel=2)]
public class Level2Class : Level1Class
{
}
public class Level3Class : Level2Class
{
}
public class Level4Class : Level3Class
{
}
控制台输出:
Level3Attribute: Inherited = False; ClassLevel = 2
Level2Attribute: Inherited = False; ClassLevel = 2
编译器似乎遵循语言规范,但运行时遵循编码的定义。
根据 .NET 4.0 中的元数据,AttributeUsageAttribute
类标有 [AttributeUsage(AttributeTargets.Class, Inherited = true)]
。因此,如果你的属性类(在你的例子中是BaseAttribute
(应用了一个AttributeUsageAttribute
(就像所有Attribute
类一样,但如果它们没有,就不要破坏任何东西 - 见下文(,那么从BaseAttribute
派生的任何类都应该继承应用于它AttributeUsage
属性。
您的 Derived
类从 Base 继承DerivedAttribute
,因为DerivedAttribute
没有应用自己的AttributeUsageAttribute
,因此反射 API 依赖于 BaseAttribute
的属性。现在,从BaseAttribute
类中取出AttributeUsageAttribute
,你会得到相同的结果,因为基System.Attribute
类标有 [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
。因此,任何属性类都将继承此属性,除非您指定其他属性类。
哇,这些是复杂的段落。属性上的属性会导致一些繁重的阅读:P