C# 语言的大多数都使得程序员者可以指定关于在程序中定义的实体的公开的消息。例如,一个类中的一个方法的可访问性,可以通过用方法修饰符public, protected, internal 和 private 对它进行修饰来指定。
C# 使得程序员可以创造声明信息的新的种类,来为各种程序实体指定声明信息,并且在运行时环境中找回属性信息。例如,一个框架也许定义了一个HelpAttribute 属性,它可以被放在例如类和方法的程序元素中来提供从程序元素到它们的文档的映射。
声明信息的新种类通过属性类(§17.1)的声明来定义,它可能有位置的和名称的参数 (§17.1.2)。声明信息使用属性(§17.2)来指定C# 程序,并且可以在运行时作为属性实例来检索 (§17.3)。
1.1 属性类
一个属性类的声明定义了一种可以放在声明中的新的属性。一个从抽象类System.Attribute 派生的类,不管是直接派生还是间接派生都是属性类。
一个属性类的声明要受下面的附加约束的影响:
· 一个非抽象属性类必须有公共的可访问性。
· 所有其中的非抽象属性类为嵌套的类型必须有公共的可访问性。
· 一个非抽象属性必须至少有一个公共构造函数。
- 一个属性类的每个公共构造函数的每个形式参数类型必须是属性参数类型 (§17.1.3)。
根据惯例,属性类被用一个后缀Attribute 修饰。一个属性的使用可以包括也可以忽略这个后缀。
1.1.1 AttributeUsage 属性
AttributeUsage 属性被用来描述一个属性类是否可以被使用。
AttributeUsage 属性有一个位置参数用来使一个属性类可以指定它可以被应用的声明的类型。例子
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class SimpleAttribute: System.Attribute
{}
定义了一个名为SimpleAttribute 的属性类,它可以被放在类声明(class-declarations )和接口声明(interface-declarations )中。例子
[Simple] class Class1 {…}
[Simple] interface Interface1 {…}
介绍了很多Simple 属性的使用。这个属性用一个名为SimpleAttribute 的类定义,但是对这个属性的使用可以忽略后缀Attribute ,因此把名称简化为Simple。 上面的例子从语义上同下面的例子相同
[SimpleAttribute] class Class1 {…}
[SimpleAttribute] interface Interface1 {…}
AttributeUsage 属性有一个AllowMultiple 命名的参数,它指出是否这个指示的属性可以为一个所给的实体被指定多次。一个可以在一个实体中被多次指定的属性被称为多次使用属性类(multi-use attribute class )。一个在一个实体中至少可以被指定一次的属性被称为单次使用属性类(single-use attribute class)。
例子
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AuthorAttribute: System.Attribute {
public AuthorAttribute(string value);
public string Value { get {…} }
}
定义了一个多次使用属性类,名为AuthorAttribute。 例子
[Author("Brian Kernighan"), Author("Dennis Ritchie")]
class Class1 {…}
介绍了一个使用了两个Author 属性的类声明。
1.1.2 位置的和名称的参数
属性类可以有位置的参数(positional parameters )和名称的参数(named parameters)。一个属性类的每个公共构造函数为属性类定义了一个有效的位置参数序列。一个属性类的每个非静态公共读写域和属性为属性类定义了一个名称的参数。
例子
[AttributeUsage(AttributeTargets.Class)]
public class HelpAttribute: System.Attribute
{
public HelpAttribute(string url) { // url is a positional parameter
…
}
public string Topic { // Topic is a named parameter
get {...}
set {...}
}
public string Url { get {…} }
}
定义了名为HelpAttribute的一个属性类,它有一个位置参数 (string url) 和一个名称的参数 (string Topic)。 只读的属性Url 并不定义一个名称的参数,它是非静态和公共的,但是由于它是只读的,所以它并没有定义一个名称的参数。
例子
[HelpAttribute("http://www.mycompany.com/…/Class1.htm")]
class Class1 {
}
[HelpAttribute("http://www.mycompany.com/…/Misc.htm", Topic ="Class2")]
class Class2 {
}
介绍了多个属性的使用。
1.1.3 属性参数类型
一个属性类的位置的和名称的参数的类型被限制于属性参数类型(attribute parameter types)。如果一个类型是下面的一种,就是一个属性类型:
· 下面类型中的一个: bool, byte, char, double, float, int, long, short, string.
· 类型object.
· 类型System.Type.
· 一个有公共可访问性的枚举类型并且其中的类型是嵌套的(如果有),而且也有公共可访问性的。
一个定义了一个不是属性参数类型的位置或名称的参数的属性类是错误的。例子
public class InvalidAttribute: System.Attribute
{
public InvalidAttribute(Class
}
public class Class1 {
...
}
是错误的,因为它定义了一个有类型Class1的位置参数的属性类,而类型Class1不是属性参数类型。
1.2 规范
一个属性是一个附加声明信息的片断,它是为一个声明指定的。属性可以为类型声明(type-declarations )、类成员声明(class-member-declarations )、枚举成员声明(enum-member-declarations )、属性访问符声明(property-accessor-declarations )和形式参数(formal-parameter )进行声明。
属性在属性片段指定,每个属性片段被包围在方括号中,有多个属性在逗号分开的列表中指定。属性被指定的顺序和它们在片段中的安排方式不重要。属性指定的 [A][B], [B][A], [A, B], 和 [B, A] 都是相同的。
attributes:
attribute-sections
attribute-sections:
attribute-section
attribute-sections attribute-section
attribute-section:
[ attribute-list ]
[ attribute-list ,]
attribute-list:
attribute
attribute-list , attribute
attribute:
attribute-name attribute-argumentsopt
attribute-name:
reserved-attribute-name
type-name
attribute-arguments:
( positional-argument-list )
( 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
问题
我们需要更新语法来把[assembly: attributes] 和 [module: attributes] 两种形式合并到一起。
一个属性由属性名称(attribute-name )和位置的和名称的参数的可选列表组成。位置的参数(如果有)领先于名称的参数。一个位置的参数由属性参数表达式(attribute-argument-expression )构成;一个名称参数由名称,跟着一个等号,跟着一个属性参数表达式(attribute-argument-expression)构成。
属性名称(attribute-name )指定了一个保留的属性和一个属性类。如果属性名称(attribute-name)的形式是类型名称(type-name ),那么它的名称必须指向一个属性类。否则,会产生一个编译时错误。例子
class Class1 {}
[Class1] class Class2 {} // Error
是错误的,因为它试图把Class1当作属性类使用,但是它不是属性类。
一般而言,属性类用后缀Attribute命名。一个形式为类型名称(type-name )的属性名称可以包含或忽略这个后缀。在属性名称和属性类的名称之间的准确匹配是首选的。例子
[AttributeUsage(AttributeTargets.All)]
public class X: System.Attribute
{}
[AttributeUsage(AttributeTargets.All)]
public class XAttribute: System.Attribute
{}
[X] // refers to X
class Class1 {}
[XAttribute] // refers to XAttribute
class Class2 {}
介绍了两个属性类,名为X 和Xattribute。 属性 [X] 指向名为X的类,而属性 [XAttribute] 指向名为 [XAttribute]的属性类。 如果去掉对类X的声明,那么所有的属性都指向名为XAttribute 的属性类:
[AttributeUsage(AttributeTargets.All)]
public class XAttribute: System.Attribute
{}
[X] // refers to XAttribute
class Class1 {}
[XAttribute] // refers to XAttribute
class Class2 {}
在同一实体中多次使用单次使用属性类是错误的。例子
[AttributeUsage(AttributeTargets.Class)]
public class HelpStringAttribute: System.Attribute
{
string value;
public HelpStringAttribute(string value) {
this.value = value;
}
public string Value { get {…} }
}
[HelpString("Description of Class1")]
[HelpString("Another description of Class1")]
public class Class1 {}
评论