C#属性.docx
《C#属性.docx》由会员分享,可在线阅读,更多相关《C#属性.docx(25页珍藏版)》请在冰点文库上搜索。
C#属性
属性299
17.1属性类299
17.1.1属性用法299
17.1.2定位和命名参数300
17.1.3属性参数类型301
17.2属性专用化301
17.3属性实例306
17.3.1属性的编译306
17.3.2属性实例的运行时检索306
17.4保留属性307
17.4.1AttributeUsage属性307
17.4.2Conditional属性308
17.4.2.1条件方法308
17.4.2.2条件属性类310
17.4.3Obsolete属性311
17.5交互操作的属性312
17.5.1与COM和Win32组件的交互操作312
17.5.2与其他.NET语言的交互操作312
17.5.2.1IndexerName属性312
1.属性
C#语言的一个重要特征是使程序员能够为程序中定义的各种实体附加一些声明性信息。
例如,类中方法的可访问性是通过使用method-modifiers(public、protected、internal和private)加以修饰来指定的。
C#使程序员可以创造新的声明性信息的种类,称为属性(attribute)。
然后,程序员可以将这种属性附加到各种程序实体,而且在运行时环境中还可以检索这些属性信息。
例如,一个框架可以定义一个名为HelpAttribute的属性,该属性可以放在某些程序元素(如类和方法)上,以提供从这些程序元素到其文档说明的映射。
属性是通过属性类(第17.1节)的声明定义的,属性类可以具有定位和命名参数(第17.1.2节)。
属性是使用属性规范(第17.2节)附加到C#程序中的实体上的,而且可以在运行时作为属性实例(第17.3节)检索。
1.1属性类
从抽象类System.Attribute派生的类(不论是直接的还是间接的)都称为属性类(attributeclass)。
一个关于属性类的声明定义一种新属性(attribute),它可以被放置在其他声明上。
按照约定,属性类的名称均带有Attribute后缀。
使用属性时可以包含或省略此后缀。
1.1.1属性用法
属性AttributeUsage(第17.4.1节)用于描述使用属性类的方式。
AttributeUsage具有一个定位参数(第17.1.2节),该参数使属性类能够指定自己可以用在那种声明上。
在以下示例中:
usingSystem;
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
publicclassSimpleAttribute:
Attribute
{
...
}
定义了一个名为SimpleAttribute的属性类,此属性类只能放在class-declaration和interface-declaration上。
在以下示例中:
[Simple]classClass1{...}
[Simple]interfaceInterface1{...}
演示了Simple属性的几种用法。
虽然此属性是用名称SimpleAttribute定义的,但在使用时可以省略Attribute后缀,从而得到简称Simple。
因此,上例在语义上等效于:
[SimpleAttribute]classClass1{...}
[SimpleAttribute]interfaceInterface1{...}
AttributeUsage还具有一个名为AllowMultiple的命名参数(第17.1.2节),此参数用于说明对于某个给定实体,是否可以多次使用该属性。
如果属性类的AllowMultiple为true,则此属性类是多次性属性类(multi-useattributeclass),可以在一个实体上多次被指定。
如果属性类的AllowMultiple为false或未指定,则此属性类是一次性属性类(single-useattributeclass),在一个实体上最多只能指定一次。
在以下示例中:
usingSystem;
[AttributeUsage(AttributeTargets.Class,AllowMultiple=true)]
publicclassAuthorAttribute:
Attribute
{
privatestringname;
publicAuthorAttribute(stringname){
this.name=name;
}
publicstringName{
get{returnname;}
}
}
定义了一个名为AuthorAttribute的多次性属性类。
在以下示例中:
[Author("BrianKernighan"),Author("DennisRitchie")]
classClass1
{
...
}
演示了一个两次使用Author属性的类声明。
AttributeUsage具有另一个名为Inherited的命名参数,此参数指示在基类上指定该属性时,该属性是否也会被从此基类派生的类所继承。
如果属性类的Inherited为true,则该属性会被继承。
如果属性类的Inherited为false,则该属性不会被继承。
如果该值未指定,则其默认值为true。
没有附加AttributeUsage属性的属性类X,例如
usingSystem;
classX:
Attribute{...}
等效于下面的内容:
usingSystem;
[AttributeUsage(
AttributeTargets.All,
AllowMultiple=false,
Inherited=true)
]
classX:
Attribute{...}
1.1.2定位和命名参数
属性类可以具有定位参数(positionalparameter)和命名参数(namedparameter)。
属性类的每个公共实例构造函数为该属性类定义一个有效的定位参数序列。
属性类的每个非静态公共读写字段和属性为该属性类定义一个命名参数。
在以下示例中:
usingSystem;
[AttributeUsage(AttributeTargets.Class)]
publicclassHelpAttribute:
Attribute
{
publicHelpAttribute(stringurl){//Positionalparameter
...
}
publicstringTopic{//Namedparameter
get{...}
set{...}
}
publicstringUrl{
get{...}
}
}
定义了一个名为HelpAttribute的属性类,它具有一个定位参数(url)和一个命名参数(Topic)。
虽然Url属性是非静态的和公共的,但由于它不是读写的,因此它并不定义命名参数。
此属性类可以如下方式使用:
[Help("
classClass1
{
...
}
[Help("Topic="Class2")]
classClass2
{
...
}
1.1.3属性参数类型
属性类的定位参数和命名参数的类型仅限于属性参数类型(attributeparametertype),它们是:
∙以下类型之一:
bool,byte,char,double,float,int,long,sbyte,short,string,uint,ulong,ushort.
∙类型object。
∙类型System.Type。
∙枚举类型,前提是该枚举类型具有public可访问性,而且所有嵌套着它的类型(如果有)也必须具有public可访问性(第17.2节)。
∙以上类型的一维数组。
1.2属性专用化
属性专用化(Attributespecification)就是将以前定义的属性应用到某个声明上。
属性本身是一段附加说明性信息,可以把它指定给某个声明。
可以在全局范围指定属性(即,在包含程序集或模块上指定属性),也可以为下列各项指定属性:
type-declaration(第9.5节)、class-member-declaration(第
10.2节)、interface-member-declaration(第13.2节)、struct-member-declaration(第11.2节)、enum-member-declaration(第14.3节)、accessor-declaration(第10.6.2节)、event-accessor-declarations(第10.7.1节)和formal-parameter-lists(第10.5.1节)。
属性是在属性节(attributesection)中指定的。
属性节由一对方括号组成,此方括号括着一个用逗号分隔的、含有一个或多个属性的列表。
在这类列表中以何种顺序指定属性,以及附加到同一程序实体的属性节以何种顺序排列等细节并不重要。
例如,属性专用化[A][B]、[B][A]、[A,B]和[B,A]是等效的。
global-attributes:
global-attribute-sections
global-attribute-sections:
global-attribute-section
global-attribute-sectionsglobal-attribute-section
global-attribute-section:
[global-attribute-target-specifierattribute-list]
[global-attribute-target-specifierattribute-list,]
global-attribute-target-specifier:
global-attribute-target:
global-attribute-target:
assembly
module
attributes:
attribute-sections
attribute-sections:
attribute-section
attribute-sectionsattribute-section
attribute-section:
[attribute-target-specifieroptattribute-list]
[attribute-target-specifieroptattribute-list,]
attribute-target-specifier:
attribute-target:
attribute-target:
field
event
method
param
property
return
type
attribute-list:
attribute
attribute-list,attribute
attribute:
attribute-nameattribute-argumentsopt
attribute-name:
type-name
attribute-arguments:
(positional-argument-listopt)
(positional-argument-list,named-argument-list)
(named-argument-list)
positional-argument-list:
positional-argument
positional-argument-list,positional-argument
positional-argument:
attribute-argument-expression
named-argument-list:
named-argument
named-argument-list,named-argument
named-argument:
identifier=attribute-argument-expression
attribute-argument-expression:
expression
如上所述,属性由一个attribute-name和一个可选的定位和命名参数列表组成。
定位参数(如果有)列在命名参数前面。
定位参数包含一个attribute-argument-expression;命名参数包含一个名称,名称后跟一个等号和一个attribute-argument-expression,这两种参数都受简单赋值规则约束。
命名参数的排列顺序无关紧要。
attribute-name用于标识属性类。
如果attribute-name的形式等同于一个type-name,则此名称必须引用一个属性类。
否则将发生编译时错误。
在以下示例中:
classClass1{}
[Class1]classClass2{}//Error
产生编译时错误,因为它试图将Class1用作属性类,而Class1并不是一个属性类。
某些上下文允许将一个属性指定给多个目标。
程序中可以利用attribute-target-specifier来显式地指定目标。
属性放置在全局级别中时,则需要global-attribute-target-specifier。
对于所有其他位置上的属性,则采用系统提供的合理的默认值,但是在某些目标不明确的情况下可以使用attribute-target-specifier来确认或重写默认值,也可以在目标明确的情况下使用属性目标说明符来确认默认值。
因此,除在全局级别之外,通常可以省略attribute-target-specifier。
对于可能造成不明确性的上下文,按下述规则处理:
∙在全局范围指定的属性可以应用于目标程序集或目标模块。
系统没有为此上下文提供默认形式,所以在此上下文中始终需要一个attribute-target-specifier。
如果存在assemblyattribute-target-specifier,则表明此属性适用于指定的目标程序集;如果存在moduleattribute-target-specifier,则表明此属性适用于指定的目标模块。
∙在委托声明上指定的属性,或者适用于所声明的委托,或者适用于它的返回值。
如果不存在attribute-target-specifier,则此属性适用于该委托。
如果存在typeattribute-target-specifier,则表明此属性适用于该委托;如果存在returnattribute-target-specifier,则表明此属性适用于返回值。
∙在方法声明上指定的属性,或者适用于所声明的方法,或者适用于它的返回值。
如果不存在attribute-target-specifier,则此属性适用于方法。
如果存在methodattribute-target-specifier,则表明此属性适用于方法;如果存在returnattribute-target-specifier,则表明此属性适用于返回值。
∙在运算符声明上指定的属性,或者适用于所声明的运算符,或者适用于它的返回值。
如果不存在attribute-target-specifier,则此属性适用于该运算符。
如果存在methodattribute-target-specifier,则表明此属性适用于该运算符;如果存在returnattribute-target-specifier,则表明此属性适用于返回值。
∙对于在省略了事件访问器的事件声明上指定的属性,它的目标对象有三种可能的选择:
所声明的事件;与该事件关联的字段(如果该事件是非抽象事件);与该事件关联的add和remove方法。
如果不存在attribute-target-specifier,则此属性适用于该事件。
如果存在eventattribute-target-specifier,则表明此属性适用于该事件;如果存在fieldattribute-target-specifier,则表明此属性适用于该字段;而如果存在methodattribute-target-specifier,则表明此属性适用于这些方法。
∙在属性或索引器声明中的get访问器声明上指定的属性,或者适用于该访问器关联的方法,或者适用于它的返回值。
如果不存在attribute-target-specifier,则此属性适用于方法。
如果存在methodattribute-target-specifier,则表明此属性适用于方法;如果存在returnattribute-target-specifier,则表明此属性适用于返回值。
∙在属性或索引器声明中的set访问器上指定的属性,或者可适用于该访问器关联的方法,或者适用于它的独立的隐式参数。
如果不存在attribute-target-specifier,则此属性适用于方法。
如果存在methodattribute-target-specifier,则表明此属性适用于该方法;如果存在paramattribute-target-specifier,则表明此属性适用于该参数;而如果存在returnattribute-target-specifier,则表明此属性适用于该返回值。
∙在事件声明的添加或移除访问器声明上指定的属性,或者适用于该访问器关联的方法,或者适用于它的独立参数。
如果不存在attribute-target-specifier,则此属性适用于方法。
如果存在methodattribute-target-specifier,则表明此属性适用于该方法;如果存在paramattribute-target-specifier,则表明此属性适用于该参数;而如果存在returnattribute-target-specifier,则表明此属性适用于该返回值。
在其他上下文中,允许包含一个attribute-target-specifier,但这样做是没有必要的。
例如,类声明既可以包括也可以省略说明符type:
[type:
Author("BrianKernighan")]
classClass1{}
[Author("DennisRitchie")]
classClass2{}
如果指定了无效的attribute-target-specifier,则会发生错误。
例如,不能将说明符param用在类声明中:
[param:
Author("BrianKernighan")]//Error
classClass1{}
按照约定,属性类的名称均带有Attribute后缀。
type-name形式的attribute-name既可以包含也可以省略此后缀。
如果发现属性类中同时出现带和不带此后缀的名称,则引用时就可能出现多义性,从而导致运行时错误。
如果在拼写attribute-name时,明确说明其最右边的identifier为逐字标识符(第2.4.2节),则它仅匹配没有后缀的属性,从而能够解决这类多义性。
在以下示例中:
usingSystem;
[AttributeUsage(AttributeTargets.All)]
publicclassX:
Attribute
{}
[AttributeUsage(AttributeTargets.All)]
publicclassXAttribute:
Attribute
{}
[X]//Error:
ambiguity
classClass1{}
[XAttribute]//ReferstoXAttribute
classClass2{}
[@X]//ReferstoX
classClass3{}
[@XAttribute]//ReferstoXAttribute
classClass4{}
演示两个分别名为X和XAttribute的属性类。
属性[X]含义不明确,因为该属性即可引用X也可引用XAttribute。
使用逐字标识符能够在这种极少见的情况下表明确切的意图。
属性[XAttribute]是明确的(尽管当存在名为XAttributeAttribute的属性类时该属性将是不明确的!
)。
如果移除了类X的声明,那么上述两个属性都将引用名为XAttribute的属性类,如下所示:
usingSystem;
[AttributeUsage(AttributeTargets.All)]
publicclassXAttribute:
Attribute
{}
[X]//ReferstoXAttribute
classClass1{}
[XAttribute]//ReferstoXAttribute
classClass2{}
[@X]//Error:
noattributenamed"X"
classClass3{}
在同一个实体中多次使用一次性属性类属于编译时错误。
在以下示例中:
usingSystem;
[AttributeUsage(AttributeTargets.Class)]
publicclassHelpStringAttribute:
Attribute
{
stringvalue;
publicHelpStringAttribute(stringvalue){
this.value=value;
}
publicstringValue{
get{...}
}
}
[HelpString("DescriptionofClass1")]
[HelpString("AnotherdescriptionofClass1")]
publicclassClass1{}
产生编译时错误,因为它试图在Class1的声明中多次使用一次性属性类HelpString。
如果表达式E满足下列所有条件,则该表达式为attribute-argument-expression:
∙E的类型是属性参数类型(第17.1.3节)。
∙在编译时,E的值可以解析为下列之一:
o常数值。
oSystem.Type对象。
oattribute-argu