自定义属性类是否继承“继承”属性用法标志

本文关键字:继承 属性 用法 标志 是否 自定义属性 | 更新日期: 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 可以继承,它从父级继承了一个。

在运行时,继承按预期执行,并且继承 BaseAttributeAttributeUsageAttribute (已继承设置为 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