C#中的Attribute学习笔记

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com

C#的Attribute的一些细节描述:

  • 1 C#的attribute,通常翻译为“ 特性 ”。经常出现的英文单词则翻译为“ 属性
  • 2 C#的attribute可以视为是一种 附着物。就像牡蛎吸附在船底或礁石上一样。这些附着物的作用是为它们的附着体,追加上一些额外的信息。而这些信息保存在附着物的体内。对,也即是被编译器编译进 程序集(assembly)元数据(Metadata) 里,在程序运行的时候,随时可以从元数据里提取出这些附加信息,来决策或影响程序的运行。
  • 3 C#的attribute不是什么别的,其本质上就是一个类,而且这个类在使用上要遵循以下的一些规则:
    • 3.1 不使用new操作符来产生实例,而是使用在方括号里调用构造函数的来产生实例。构造函数的参数是一定要写的,有几个就得写几个。且参数的顺序不能错
    • 3.2 方括号必需紧挨着放置在被附着目标的前面。
    • 3.3 因为方括号里空间有限,不能像使用new那样先构造对象后,再对对象的 属性(Property) 一一赋值。因此,对Attribute实例属性的赋值,也需写在构造函数的圆括号里。但对property的赋值,可有可无,这是因为编译器肯定对property设置一个默认值。对多个property的赋值先后顺序也没有关系。

能被Attribute所附着的目标种类

Attribute能附着的目标的种类在枚举类型AttributeTargets中有定义,代码如下:

 1namespace System
 2{
 3    [ComVisible(true)]
 4    [Flags]
 5    public enum AttributeTargets
 6    {
 7        Assembly = 1,
 8        Module = 2,
 9        Class = 4,
10        Struct = 8,
11        Enum = 16,
12        Constructor = 32,
13        Method = 64,
14        Property = 128,
15        Field = 256,
16        Event = 512,
17        Interface = 1024,
18        Parameter = 2048,
19        Delegate = 4096,
20        ReturnValue = 8192,
21        GenericParameter = 16384,
22        All = 32767
23    }
24}

从上面的定义可以看到,如果有指定多个attribute所能附着的目标类型的话,在设置AttributeTragets类型值是用按位或操作符连起来就好,例如:AttributeTargets.Method | AttributeTargets.Class。  默认情况下,当我们声明并定义一个新Attribute类时,它的可附着目标就是AttributeTargets.All。如果你明确地指定只用于若干种目标类型的话,要如下代码所示进行操作:

1 [AttributeUsage(AttributeTargets.Class|AttributeTargets.Field)]
2class CustomAttribute : System.Attribute     
3{
4    // CustomAttribute类的具体实现
5}

要声明某个自定义特性类附着于哪些具体的目标类型,需要用到另一个特性类AttributeUsage去声明之。通过使用AttributeUsage类,还能指定:

  • 1.如果一个Attribute附着在了某个类上,可以指定该Attribute随着继承关系,附着、或者不附着在派生类上。
  • 2 可以指定 一个 Attribute的 多个 实例,附着在 同一个 目标上。

如下代码所示:

1[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = false,AllowMultiple = true)]
2class CustomAttribute : System.Attribute     
3{
4    // CustomAttribute类的具体实现
5}

创建自定义特性

.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

创建并使用自定义特性包含四个步骤:

  • 声明自定义特性
  • 构建自定义特性
  • 在目标程序元素上应用自定义特性
  • 通过反射访问特性

声明自定义特性

一个新的自定义特性应派生自System.Attribute类。如下代码所示:

1// 一个自定义特性 BugFix 被赋给类及其成员
2[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property,AllowMultiple = true)]
3public class DeBugInfo : System.Attribute

上面的代码已声明了一个名为DeBugInfo的自定义特性。

构建自定义特性

让我们构建一个名为DeBugInfo的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:

  • bug 的代码编号
  • 辨认该 bug 的开发人员名字
  • 最后一次审查该代码的日期
  • 一个存储了开发人员标记的字符串消息

DeBugInfo类将带有用于存储前三个信息的私有property,共三个,以及一个用于存储消息的公有property。所以bug编号、开发人员名字和审查日期将是DeBugInfo类的必需的 定位( positional)参数 ,消息将是一个可选的 具名(named)参数

每个特性必须至少有一个构造函数。必需的定位参数,应通过构造函数传递。下面的代码演示了DeBugInfo类:

 1// 一个自定义特性 BugFix 被赋给类及其成员
 2[AttributeUsage(AttributeTargets.Class |
 3AttributeTargets.Constructor |AttributeTargets.Field |
 4AttributeTargets.Method |AttributeTargets.Property,
 5AllowMultiple = true)]
 6public class DeBugInfo : System.Attribute
 7{
 8    private int bugNo;
 9    private string developer;
10    private string lastReview;
11    public string message;
12
13    public DeBugInfo(int bg, string dev, string d)
14    {
15        this.bugNo = bg;
16        this.developer = dev;
17        this.lastReview = d;
18    }
19
20    public int BugNo{get {return bugNo;}}
21
22    public string Developer{get{return developer;}}
23
24    public string LastReview{get{return lastReview;}}
25
26    public string Message
27    {
28        get{return message;}
29        set{message = value;}
30    }
31}

在目标程序元素上应用自定义特性

通过把特性放置在紧接着它的目标类型之前,来应用该特性:

 1[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
 2[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
 3class Rectangle
 4{
 5    // 成员变量
 6    protected double length;
 7    protected double width;
 8  
 9    public Rectangle(double l, double w)
10    {
11        length = l;
12        width = w;
13    }
14
15    [DeBugInfo(55, "Zara Ali", "19/10/2012",Message = "Return type mismatch")]
16    public double GetArea()
17    {
18        return length * width;
19    }
20  
21    [DeBugInfo(56, "Zara Ali", "19/10/2012")]
22    public void Display()
23    {
24        Console.WriteLine("Length: {0}", length);
25        Console.WriteLine("Width: {0}", width);
26        Console.WriteLine("Area: {0}", GetArea());
27    }
28}

通过反射访问特性

在本实例中,使用前面定义的DeBugInfo特性,将其附着在Rectangle类中。并使用 反射(Reflection) 机制来读取 Rectangle类中的元数据。

  1using System;
  2using System.Reflection;
  3
  4namespace BugFixApplication
  5{
  6   // 一个自定义特性 BugFix 被赋给类及其成员
  7   [AttributeUsage(AttributeTargets.Class|AttributeTargets.Constructor| 
  8   AttributeTargets.Field|AttributeTargets.Method|AttributeTargets.Property,
  9   AllowMultiple = true)]
 10
 11   public class DeBugInfo : System.Attribute
 12   {
 13      private int bugNo;
 14      private string developer;
 15      private string lastReview;
 16      public string message;
 17
 18      public DeBugInfo(int bg, string dev, string d)
 19      {
 20         this.bugNo = bg;
 21         this.developer = dev;
 22         this.lastReview = d;
 23      }
 24
 25      public int BugNo{get{return bugNo;}}
 26      public string Developer{get{return developer;}}     
 27      public string LastReview{get{return lastReview;}}
 28      
 29      public string Message
 30      {
 31         get{return message;}
 32         set{message = value;}
 33      }
 34   }
 35
 36   [DeBugInfo(45, "Zara Ali", "12/8/2012",Message = "Return type mismatch")]
 37   [DeBugInfo(49, "Nuha Ali", "10/10/2012",Message = "Unused variable")]
 38   class Rectangle
 39   {
 40      // 成员变量
 41      protected double length;
 42      protected double width;
 43
 44      public Rectangle(double l, double w)
 45      {
 46         length = l;
 47         width = w;
 48      }
 49
 50      [DeBugInfo(55, "Zara Ali", "19/10/2012",Message = "Return type mismatch")]
 51      public double GetArea()
 52      {
 53         return length * width;
 54      }
 55
 56      [DeBugInfo(56, "Zara Ali", "19/10/2012")]
 57      public void Display()
 58      {
 59         Console.WriteLine("Length: {0}", length);
 60         Console.WriteLine("Width: {0}", width);
 61         Console.WriteLine("Area: {0}", GetArea());
 62      }
 63   }
 64   
 65   class ExecuteRectangle
 66   {
 67      static void Main(string[] args)
 68      {
 69         Rectangle r = new Rectangle(4.5, 7.5);
 70         r.Display();
 71         Type type = typeof(Rectangle);
 72
 73         foreach (Object attributes in type.GetCustomAttributes(false))
 74         {
 75            DeBugInfo dbi = (DeBugInfo)attributes;
 76
 77            if (null != dbi)
 78            {
 79               Console.WriteLine("Bug no: {0}", dbi.BugNo);
 80               Console.WriteLine("Developer: {0}", dbi.Developer);
 81               Console.WriteLine("Last Reviewed: {0}",dbi.LastReview);
 82               Console.WriteLine("Remarks: {0}", dbi.Message);
 83            }
 84         }
 85         
 86         foreach (MethodInfo m in type.GetMethods())
 87         {
 88            foreach (Attribute a in m.GetCustomAttributes(true))
 89            {
 90               DeBugInfo dbi = (DeBugInfo)a;
 91               if (null != dbi)
 92               {
 93                  Console.WriteLine("Bug no: {0}, for Method: {1}",dbi.BugNo, m.Name);
 94                  Console.WriteLine("Developer: {0}", dbi.Developer);
 95                  Console.WriteLine("Last Reviewed: {0}",dbi.LastReview);
 96                  Console.WriteLine("Remarks: {0}", dbi.Message);
 97               }
 98            }
 99         }
100         Console.ReadLine();
101      }
102   }
103}

当上面的代码被编译和执行时,它会产生下列结果:

 1Length: 4.5
 2Width: 7.5
 3Area: 33.75
 4Bug No: 49
 5Developer: Nuha Ali
 6Last Reviewed: 10/10/2012
 7Remarks: Unused variable
 8Bug No: 45
 9Developer: Zara Ali
10Last Reviewed: 12/8/2012
11Remarks: Return type mismatch
12Bug No: 55, for Method: GetArea
13Developer: Zara Ali
14Last Reviewed: 19/10/2012
15Remarks: Return type mismatch
16Bug No: 56, for Method: Display
17Developer: Zara Ali
18Last Reviewed: 19/10/2012
19Remarks: 

参考网页

深入浅出Attribute 上

深入浅出Attribute 中

深入浅出Attribute 下

C# 特性(Attribute)