C#中的方括号[](特性、属性)

约定:css

     1.”attribute” 和 ”attributes” 均不翻译 html

     2.”property” 译为“属性” c#

     3.msdn 中的原句不翻译 ide

     4.”program entity” 译为 ” 语言元素 函数

Attributes in C# 学习

介绍 this

Attributes 是一种新的描述信息,咱们既可使用 attributes 来定义设计期信息(例如 帮助文件,文档的 URL ),还能够用 attributes 定义运行时信息(例如,使 XML 中的元素与类的成员字段关联起来)。咱们也能够用 attributes 来建立一个“自描述”的组件。在这篇指南中咱们将明白怎么建立属性并将其绑定至各类语言元素上,另外咱们怎样在运行时环境下获取到 attributes 的一些信息。 spa

定义 翻译

MSDN 中作以下定义 (ms-help://MS.MSDNQTR.2002APR.1033/csspec/html/vclrfcsh ARP spec_17_2.htm) 设计

"An attribute is a piece of additional declarative information that is specified for a declaration." 

使用预约义 Attributes

c# 中已有一小组预约义的 attributes ,在咱们学习怎样建立自定义 attributes 前,先来了解下在咱们的代码中使用那些预约义的 attributes.

 1 using  System; 
 2 
 3 public   class  AnyClass  
 4 
 5 { 
 6     [Obsolete( " Don't use Old method, use New method " ,  true )] 
 7 
 8 static   void  Old( ) { } 
 9 
10 static   void  New( ) { } 
11 
12 public   static   void  Main( )  
13     { 
14         Old( ); 
15     } 
16 } 

仔细看下该实例,在该实例中咱们用到了 ”Obsolete”attribute ,它标记了一个不应再被使用的语言元素 ( 译者注:这里的元素为方法 ) ,该属性的第一个参数是 string 类型,它解释为何该元素被荒弃,以及咱们该使用什么元素来代替它。实际中,咱们能够书写任何其它文原本代替这段文本。第二个参数是告诉编译器把依然使用这被标识的元素视为一种错误,这就意味着编译器会所以而产生一个警告。

当咱们试图编译上面的上面的程序,咱们会获得以下错误:

AnyClass.Old()' is obsolete: 'Don't use Old method,   use New method'

开发自定义 Attributes

如今咱们即将了解怎么开发自定义的 attributes 。这儿有个小小处方,有它咱们就能够学会建立自定义的 attributes 。

C# 中,咱们的 attribute 类都派生于 System.Attribute 类 ( A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration ) ,咱们就这么行动吧。

using  System; 

public   class  HelpAttribute : Attribute 

{ 

} 

无论你是否相信我,就这样咱们就已经建立了一个自定义 attribute 。如今就能够用它来装饰咱们的类了,就像咱们使用 obsolete attribute 同样。

[Help()] 

public   class  AnyClass 

{ 

} 

注意:按惯例咱们是用 ”Attribute“ 做为 attribute 类名的后缀,然而,当咱们当咱们把 attribute 绑定到某语言元素时,是不包含 “Attribute“ 后缀的。编译器首先在 System.Attribute 的继承类中查找该 attribute ,若是没有找到,编译器会把 “Attribute“ 追加到该 attribute 的名字后面,而后查找它。

可是迄今为止,该 attribute 没有任何用处。为了使它有点用处,让咱们在它里面加点东西吧。

using  System; 
public   class  HelpAttribute : Attribute 
{ 
public  HelpAttribute(String Descrition_in) 
    { 
this .description  =  Description_in; 
    } 
protected  String description; 
public  String Description  
    { 
get 
        { 
return   this .description; 

        }             
    }     
} 
[Help( " this is a do-nothing class " )] 
public   class  AnyClass 
{ 
} 

在上面的例子中,咱们在 attribute 类中添加了一个属性,在最后一节中,咱们将在运行时查询该属性。

定义或控制自定义 Attribute 的用法

AttributeUsage 类是另外一预约义类 ( 译者注: attribute 类自己用这个 atrribute System.AttributeUsage 来标记 ) ,它将帮助咱们控制咱们自定义 attribute 的用法,这就是,咱们能为自定义的 attribute 类定义 attributes 。

它描述了一个自定义 attribute 类能被怎样使用。

AttributeUsage 提供三个属性,咱们能将它们放置到咱们的自定义 attribute 类上, 第一个特性是:

ValidOn

经过这个属性,咱们能指定咱们的自定义 attribute 能够放置在哪些语言元素之上。这组咱们能把自定义 attribute 类放置其上的语言元素被放在枚举器 AttributeTargets 中。咱们可使用 bitwise( 译者注:这个词不知道怎么翻译好,但他的意思是能够这么用 : [AttributeUsage ( ( AttributeTargets)4 , AllowMultiple =   false , Inherited  =   false  )], 4 表明就是 “ class ” 元素,其它诸如 1 表明“ assembly ”, 16383 表明“ all ”等等 ) 或者 ”.” 操作符绑定几个 AttributeTargets 值。 (译者注:默认值为 AttributeTargets.All )

AllowMultiple

该属性标识咱们的自定义 attribte 能在同一语言元素上使用屡次。 ( 译者注:该属性为 bool 类型,默认值为 false ,意思就是该自定义 attribute 在同一语言元素上只能使用一次 )

 

Inherited

咱们可使用该属性来控制咱们的自定义 attribute 类的继承规则。该属性标识咱们的自定义 attribute 是否能够由派生类继承。( (译者注:该属性为 bool 类型,默认值为 false ,意思是不能继承)

让咱们来作点实际的东西吧,咱们将把 AttributeUsage attribute 放置在咱们的 help attribute 上并在它的帮助下,咱们来控制 help attribute 的用法。

using  System; 

[AttributeUsage(AttributeTargets.Class, AllowMultiple  =   false , Inherited  =   false  )] 

public   class  HelpAttribute : Attribute 

{ 

public  HelpAttribute(String Description_in) 

    { 

    this .description  =  Description_in; 

    } 

protected  String description; 

public  String Description 

    { 

    get 

    { 

    return   this .description; 

     }             

    }     

} 

首先咱们注意 AttributeTargets.Class . 它规定这个 help attribute 只能放置在语言元素 ”class” 之上。这就意味着,下面的代码将会产生一个错误。

AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.

如今试着把它绑定到方法。

[Help( " this is a do-nothing class " )] 

public   class  AnyClass 

{ 

    [Help( " this is a do-nothing method " )]     // error 

public   void  AnyMethod() 

    { 

    } 

}  

咱们可使用 AttributeTargets.All 来容许 Help attribute 能够放置在任何预约义的语言元素上,那些可能的语言元素以下

  • Assembly, 
  • Module, 
  • Class, 
  • Struct, 
  • Enum, 
  • Constructor, 
  • Method, 
  • Property, 
  • Field,
  • EVE nt, 
  • Interface, 
  • Parameter, 
  • Delegate, 
  • All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | EVE nt | Interface | Parameter | Delegate,
  • ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | EVE nt | Delegate | Interface )
  • ~ 如今考虑下 AllowMultiple = false . 这就规定该 attribute 不能在同一语言元素上放置屡次
  • [Help( " this is a do-nothing class " )] 
    
    [Help( " it contains a do-nothing method " )] 
    
    public   class  AnyClass 
    
    { 
    
        [Help( " this is a do-nothing method " )]         // error 
    
    public   void  AnyMethod() 
    
        { 
    
        } 
    
    } 

    它产生了一个编译错误:

    AnyClass.cs: Duplicate 'Help' attribute

    Ok !如今咱们该讨论下最后那个属性了, ”Inherited”, 指出当把该 attribute 放置于一个基类之上,是否派生类也继承了该 attribute 。若是绑定至某个 attribute 类的 ”Inherited” 被设为 true, 那么该 attribute 就会被继承,然而若是绑定至某个 attribute 类的 ”Inherited” 被设为 false 或者没有定义,那么该 attribute 就不会被继承。

    让咱们假设有以下的类关系。

    • [Help( " BaseClass " )]  
      
      public   class  Base 
      
      { 
      
      } 
      
      
      
      public   class  Derive :  Base 
      
      { 
      
      } 

      咱们有四种可能的绑定 :

      • [AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = false )] 
      • [AttributeUsage(AttributeTargets.Class, AllowMultiple = true , Inherited = false)
      • [AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = true )] 
      • [AttributeUsage(AttributeTargets.Class, AllowMultiple = true , Inherited = true)

      第一种状况

      若是咱们查询(咱们将在后面来了解如何在运行时来查询 attributes )派生类中的 help attribute ,咱们将不可能查询到由于 ”Inherited” 被设为了 false 。

      第二种状况

      第二种状况没有什么不一样,由于其 ”Inherited” 也被设为了 false 。

      第三种状况

      为了解释第三种和第四种状况,让咱们为派生类也绑定同一 attribute 。

    • [Help( " BaseClass " )]  
      
      public   class  Base 
      
      { 
      
      } 
      
      [Help( " DeriveClass " )]  
      
      public   class  Derive :  Base 
      
      { 
      
      } 

      如今咱们查询相关的 help attribute ,咱们将仅仅能够获得派生类的 attribute ,为何这样是由于 help attribute 虽然容许被继承,但不能屡次在同一语言元素上使用,因此基类中的 help attribute 被派生类的 help attribute 重写了。

      第四种状况

      在第四种状况中,当咱们查询派生类的 help attribute 时,咱们能够获得两个 attributes ,固然是由于 help attribute 既容许被继承,又容许在同一语言元素上屡次使用的结果。

      注意: AttributeUsage attribute 仅应用在那种是 System.Attribute 派生的 attriubte 类并且绑定值该 attriubte 类的 AllowMultiple 和 Inherited 均为 false 上才是有效的。

      可选参数 vs. 命名参数

      可选参数是 attribute 类构造函数的参数。它们是强制的,必须在每次在 attribute 绑定至某语言元素时提供一个值。而另外一方面,命名参数却是真正的可选参数,不是在 attribute 构造函数的参数。

      为了更加详细的解释,让咱们在 Help 类中添加另外的属性。

    • [AttributeUsage(AttributeTargets.Class, AllowMultiple  =   false , 
      
       Inherited  =   false )] 
      
      public   class  HelpAttribute : Attribute 
      
      { 
      
      public  HelpAttribute(String Description_in) 
      
          { 
      
      this .description  =  Description_in; 
      
      this .verion  =   " No Version is defined for this class " ; 
      
          } 
      
      protected  String description; 
      
      public  String Description 
      
          { 
      
      get 
      
              { 
      
      return   this .description; 
      
              } 
      
          } 
      
      protected  String version; 
      
      public  String Version 
      
          { 
      
      get 
      
              { 
      
      return   this .version; 
      
              } 
      
      // if we  EVE r want our attribute user to set this property,  
      
      // we must specify set method for it  
      
      set 
      
              { 
      
      this .verion  =  value; 
      
              } 
      
          } 
      
      } 
      
      [Help( " This is Class1 " )] 
      
      public   class  Class1 
      
      { 
      
      } 
      
      
      
      [Help( " This is Class2 " , Version  =   " 1.0 " )] 
      
      public   class  Class2 
      
      { 
      
      } 
      
      
      
      [Help( " This is Class3 " , Version  =   " 2.0 " ,  
      
       Description  =   " This is do-nothing class " )] 
      
      public   class  Class3 
      
      { 
      
      } 

      当咱们在 Class1 中查询 Help attribute 已经它的属性,咱们将获得:

      Help.Description : This is Class1

      Help.Version :No Version is defined for this class

      由于咱们没有为 Version 这个属性定义任何任何值,因此在构造函数中设定的值被咱们查询出来了。若是没有定义任何值,那么就会赋一个该类型的默认值(例如:若是是 int 型,默认值就是 0 )。

      如今,查询 Class2 的结果是:

      Help.Description : This is Class2

      Help.Version :   1.0

      咱们不能为了可选参数而使用多个构造函数,应该用已命名参数来代替。咱们之因此称它们为已命名的,是由于当咱们在构造函数为它们提供值时,咱们必须命名它们。例如,在第二个类中,咱们如是定义 Help 。

      [Help( "This is Class2" , Version = "1.0" )]

      AttributeUsage 例子中 , 参数 ”ValidOn” 是可选参数,而 “Inherited“ 和 “AllowMultiple“ 是命名参数。

      注意:为了在 attribute 的构造函数中设定命名参数的值,咱们必须为相应的属性提供一个 set 方法不然会引发编译期错误:

      'Version' : Named attribute argument can't be a read only property

      如今,咱们在 Class3 中查找 Help attribute 及其属性会发生什么呢?结果是跟上面提到的相同的编译期错误。

      'Desciption' : Named attribute argument can't be a read only property

      如今咱们修改下 Help 类,为属性 ”Description” 加一个 set 方法。如今的输出就是:

      Help.Description : This is do-nothing class

      Help.Version : 2.0

      在屏幕后面究竟发生了什么呢?首先带有可选参数的构造函数被调用,而后,每一个命名参数的 set 方法被调用,在构造函数中赋给命名参数的值被 set 方法所覆写。

      参数类型

      一个 attribute 类的参数类型被限定在以下类型中:

      • bool
      • byte, 
      • char
      • double
      • float ,
      • int
      • long
      • short
      • string 
      • System.Type 
      • object 
      • An enum type, provided that it and any types in which it is nested are publicly accessible. A one-dimensional array involving any of the types listed above 

      Attributes 标记

      假设,咱们想把 Help attribute 绑定至元素 assembly 。第一个问题是咱们要把 Help attribute 放在哪儿才能让编译器肯定该 attribute 是绑定至整个 assembly 呢?考虑另外一种状况,咱们想把 attribute 绑定至一个方法的返回类型上,怎样才能让编译器肯定咱们是把 attribute 绑定至方法的返回类型上,而不是整个方法呢?

      为了解决诸如此类的含糊问题,咱们使用 attribute 标识符,有了它的帮助,咱们就能够确切地申明咱们把 attribute 绑定至哪个语言元素。

      例如 :

      [ assembly: Help( "this a do-nothing assembly" )]

      这个在 Help attribute 前的 assembly 标识符确切地告诉编译器,该 attribute 被绑定至整个 assembly 。可能的标识符有:  

      • assembly
      • module
      • type
      • method
      • property
      • EVE nt
      • field
      • param
      • return

      在运行时查询 Attributes

      如今咱们明白怎么建立 attribtes 和把它们绑定至语言元素。是时候来学习类的使用者该如何在运行时查询这信息。

      为了查询一语言元素上绑定的 attributes ,咱们必须使用反射。反射有能力在运行时发现类型信息。

      咱们可使用 .NET Framework Reflection APIs 经过对整个 assembly 元数据的迭代,列举出 assembly 中全部已定义的类,类型,还有方法。

      记住那旧的 Help attribute 和 AnyClass 类。

    • using  System; 
      
      using  System.Reflection; 
      
      using  System.Diagnostics; 
      
      
      
      // attaching Help attribute to entire assembly 
      
      [assembly : Help( " This Assembly demonstrates custom attributes  
      
       creation and their run - time query. " )] 
      
      
      
      // our custom attribute class 
      
      public   class  HelpAttribute : Attribute 
      
      { 
      
      public  HelpAttribute(String Description_in) 
      
          { 
      
      // 
      
      //  TODO: Add constructor logic here 
      
      this .description  =  Description_in; 
      
      // 
      
          } 
      
      protected  String description; 
      
      public  String Description 
      
          { 
      
      get 
      
              { 
      
      return   this .deescription; 
      
      
      
              }             
      
          }     
      
      } 
      
      // attaching Help attribute to our AnyClass 
      
      [HelpString( " This is a do-nothing Class. " )] 
      
      public   class  AnyClass 
      
      { 
      
      // attaching Help attribute to our AnyMethod 
      
          [Help( " This is a do-nothing Method. " )] 
      
      public   void  AnyMethod() 
      
          { 
      
          } 
      
      // attaching Help attribute to our AnyInt Field 
      
          [Help( " This is any Integer. " )] 
      
      public   int  AnyInt; 
      
      } 
      
      class  QueryApp 
      
      { 
      
      public   static   void  Main() 
      
          { 
      
          } 
      
      } 

      咱们将在接下来的两节中在咱们的 Main 方法中加入 attribute 查询代码。

      查询程序集的 Attributes

      在接下来的代码中,咱们先获得当前的进程名称,而后用 Assembly 类中的 LoadForm ()方法加载程序集,再有用 GetCustomAttributes ()方法获得被绑定至当前程序集的自定义 attributes ,接下来用 foreach 语句遍历全部 attributes 并试图把每一个 attribute 转型为 Help attribute (即将转型的对象使用 as 关键字有一个优势,就是当转型不合法时,咱们将不需担忧会抛出异常,代之以空值( null )做为结果),接下来的一行就是检查转型是否有效,及是否是为空,跟着就显示 Help attribute 的“ Description ”属性。

    • class  QueryApp 
      
      { 
      
      public   static   void  Main() 
      
          { 
      
              HelpAttribute HelpAttr; 
      
      
      // Querying Assembly Attributes 
      
              String assemblyName; 
      
              Process p  =  Process.GetCurrentProcess(); 
      
              assemblyName  =  p.ProcessName  +   " .exe " ; 
      
      
              Assembly a  =  Assembly.LoadFrom(assemblyName); 
      
      
      foreach  (Attribute attr  in  a.GetCustomAttributes( true )) 
              { 
      
                  HelpAttr  =  attr  as  HelpAttribute; 
      
      if  ( null   !=  HelpAttr) 
      
                  { 
      
                      Console.WriteLine( " Description of {0}:\n{1} " ,  
      
                                        assemblyName,HelpAttr.Description); 
      
                  } 
      
              } 
      
      } 
      
      } 

      程序输出以下:

      Description of QueryAttribute.exe:

      This Assembly demonstrates custom attributes creation and

      their run-time query.

      Press any key to continue

      查询类、方法、类成员的 Attributes

      下面的代码中,咱们唯一不熟悉的就是 Main ()方法中的第一行。

      Type type = typeof (AnyClass);

      它用 typeof 操做符获得了一个与咱们 AnyClass 类相关联的 Type 型对象。剩下的查询类 attributes 代码就与上面的例子是类似的,应该不要解释了吧(我是这么想的)。

      为查询方法和类成员的 attributes, 首先咱们获得全部在类中存在的方法和成员,而后咱们查询与它们相关的全部 attributes ,这就跟咱们查询类 attributes 同样的方式。

    • class  QueryApp 
      
      { 
      
      public   static   void  Main() 
      
          { 
      
      
      
              Type type  =   typeof (AnyClass); 
      
              HelpAttribute HelpAttr; 
      
      
      
      
      
      // Querying Class Attributes 
      
      foreach  (Attribute attr  in  type.GetCustomAttributes( true )) 
      
              { 
      
                  HelpAttr  =  attr  as  HelpAttribute; 
      
      if  ( null   !=  HelpAttr) 
      
                  { 
      
                      Console.WriteLine( " Description of AnyClass:\n{0} " ,  
      
                                        HelpAttr.Description); 
      
                  } 
      
              } 
      
      // Querying Class-Method Attributes   
      
      foreach (MethodInfo method  in  type.GetMethods()) 
      
              { 
      
      foreach  (Attribute attr  in  method.GetCustomAttributes( true )) 
      
                  { 
      
                      HelpAttr  =  attr  as  HelpAttribute; 
      
      if  ( null   !=  HelpAttr) 
      
                      { 
      
                          Console.WriteLine( " Description of {0}:\n{1} " ,  
      
                                            method.Name,  
      
                                            HelpAttr.Description); 
      
                      } 
      
                  } 
      
              } 
      
      // Querying Class-Field (only public) Attributes 
      
      foreach (FieldInfo field  in  type.GetFields()) 
      
              { 
      
      foreach  (Attribute attr  in  field.GetCustomAttributes( true )) 
      
                  { 
      
                      HelpAttr =  attr  as  HelpAttribute; 
      
      if  ( null   !=  HelpAttr) 
      
                      { 
      
                          Console.WriteLine( " Description of {0}:\n{1} " , 
      
                                            field.Name,HelpAttr.Description); 
      
                      } 
      
                  } 
      
              } 
      
          } 
      
      } 

      The output of the following program is.

      Description of AnyClass:

      This is a do-nothing Class.

      Description of AnyMethod:

      This is a do-nothing Method.

      Description of AnyInt:

      This is any Integer.

      Press any key to continue

相关文章
相关标签/搜索