是错误的,因为它试图在Class1的声明中多次使用HelpString,而HelpString是一个单次属性类。 如果所有下面的语句都为真,表达式E就是一个属性参数表达式: · E的类型是一个属性参数类型 (§17.1.3)。 · 在编译时,E的数值可以被分解为下面的一个: · 一个常数数据。 · 一个System.Type 对象。 · 一个属性参数表达式的一维数组。 1.3 属性实例 一个属性实例是一个在运行时代表一个属性的实例。一个属性用一个属性、位置参数和名称参数定义。一个属性实例是一个属性类的实例,它用位置和名称参数来初始化。 属性实例的搜索涉及到所有编译时和运行时过程,就像在下面的章节中所描述的一样。 1.3.1 一个属性的编译 一个有属性类T,位置参数列表P和名称参数列表N的属性的编译过程包括下面几步: · 对形式为new T(P)的对象创建表达式进行编译,是按照编译时过程步骤进行。这些步骤或是会产生一个编译时错误,或是确定一个可以在运行时调用的T的构造函数。把这个构造函数称为C。 · 如果上面步骤中决定的构造函数没有公共的可访问性,就会发生一个编译时错误。 · 对每个N中的名称参数Arg: · 把Name 作为名称参数Arg的标识符。 · Name 必须标识一个T中的非静态读写公共域或属性。如果T没有那样的域或属性,就会发生一个编译时错误。 · 为属性实例的运行时实例化保持下面的信息:属性类T,T中的构造函数C,位置参数列表P和名称参数列表N。 1.3.2 一个属性实例的运行时检索 对属性的编译产生了一个属性类T,T的构造函数C,位置参数列表P和名称参数列表N。给出这些信息,一个属性实例就可以在运行时按照下面的步骤进行检索: · 为执行一个形式为T(P)的对象创建表达式,跟随运行时过程步骤,在编译时确定构造函数C的使用。这些步骤或导致一个异常,或产生一个实例T,把这个实例称作O。 · 对于每个N中的名称参数Arg in N,以下面的顺序: · 让Name 作为名称参数Arg 的标识符。如果Name 没有在O中指定一个非静态公共读写域或属性,那么会抛出一个异常。 · 让 Value 作为对Arg 属性参数表达式求值得结果。 · 如果 Name 确定了一个O中的域,那么把这个域设置为数据Value。 · 否则, Name 确定一个O中的属性。把这个属性设置为数据Value。 结果是O,一个属性类T的实例,它被初始化为有位置参数列表P和名称参数列表N。 1.4 保留的属性 属性的一小部分在某些方面影响语言。这些属性包括: · System.AttributeUsageAttribute, 它被用来描述一个可以使用属性类的方法。 · System.ConditionalAttribute, 它被用来定义条件方法。 · System.ObsoleteAttribute, 它被用来把一个成员标注为废弃的。 1.4.1 AttributeUsage 属性 AttributeUsage 属性被用来描述一种属性类可以被使用的方式。 一个用AttributeUsage 属性声明的类必须从System.Attribute派生,或者直接或者间接。否则,会产生一个编译时错误。 [AttributeUsage(AttributeTargets.Class)]public class AttributeUsageAttribute: System.Attribute{public AttributeUsageAttribute(AttributeTargets validOn) {…} public AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) {…} public virtual bool AllowMultiple { get {…} set {…} } public virtual bool Inherited { get {…} set {…} } public virtual AttributeTargets ValidOn { get {…} }} public enum AttributeTargets{Assembly = 0x0001,Module = 0x0002,Class = 0x0004,Struct = 0x0008,Enum = 0x0010,Constructor = 0x0020,Method = 0x0040,Property = 0x0080,Field = 0x0100,Event = 0x0200,Interface = 0x0400,Parameter = 0x0800,Delegate = 0x1000, All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate, ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface,} 1.4.2 条件属性 条件属性使得条件方法的定义变为可能。条件属性在一个预处理标识符的形式中指定一个条件。对于一个条件方法的调用或者被包括或者被忽略,要根据在调用的地方这个符号是否被定义来决定。如果符号被定义了,那么方法调用被包括,如果符号没有定义,那么调用被忽略。 [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]public class ConditionalAttribute: System.Attribute{public ConditionalAttribute(string conditionalSymbol) {…} public string ConditionalSymbol { get {…} }} 一个条件方法要受下面的约束: · 条件方法必须是一个类声明中的方法。如果条件属性在接口方法中被指定,那么就会发生一个编译时错误。 · 条件方法必须返回一个void类型。 · 条件方法不能用override修饰符来标注。一个条件方法可以用virtual修饰符来标注。覆盖这样的一个方法是隐含地有条件的,并且不能用条件属性来显式地标注。 · 条件方法不能是一个接口方法的实现程序,否则,会产生编译时错误。 而且,如果条件方法被用在代表创建表达式中,也会产生一个编译时错误。例子 #define DEBUG class Class1 {[Conditional("DEBUG")]public static void M() { Console.WriteLine("Executed Class1.M");}} class Class2{public static void Test() { Class1.M();}} 把Class1.M 声明为一个条件方法。Class2的Test方法调用这个方法。由于预定义的符号DEBUG已经被定义,因此如果Class2.Test 被调用,它就会调用M。如果符号DEBUG没有被定义,那么Class2.Test将不会调用Class1.M。 注意,下面这点很重要,包含或去掉一个对条件方法的调用,是被在调用的地方的预定义标识符所控制的。在例子中 // Begin class1.csclass Class1 { [Conditional("DEBUG")] public static void F() { Console.WriteLine("Executed Class1.F"); }}// End class1.cs // Begin class2.cs#define DEBUG class Class2{ public static void G { Class1.F(); // F is called }}// End class2.cs // Begin class3.cs#undef DEBUG class Class3{ public static void H { Class1.F(); // F is not called }}// End class3.cs 类Class2 和 Class3每个都包含对条件方法Class1.F的调用,这个条件是基于DEBUG是存在的还是不存在地。由于这个符号在Class2 的上下文中被定义了,但是没有在Class3中定义,因此对Class2中的F的调用被实际进行,而对Class3中的F的调用被忽略了。 在一个继承链中使用条件方法可能会很混乱。通过base对条件方法的调用,形式为base.M ,要受通常条件方法调用规则的限制。在例子中 // Begin class1.csclass Class1 { [Conditional("DEBUG")] public virtual void M() { Console.WriteLine("Class1.M executed"); }}// End class1.cs // Begin class2.csclass Class2: Class1{ public override void M() { Console.WriteLine("Class2.M executed"); base.M(); // base.M is not called! }}// End class2.cs // Begin class3.cs#define DEBUG class Class3{ public static void Test() { Class2 c = new Class2(); c.M(); // M is called }}// End class3.cs Class2 包含一个在它的基类中定义的对M的调用。因为基本方法是根据符号DEBUG的存在来确定,而它没有被定义,所以这个调用就被忽略了。因此,写到控制台的方法只是"Class2.M executed"。聪明地使用pp-declarations 就可以解决这样的问题。 1.4.3 废弃的属性 废弃的属性被用于对不再使用的程序元素进行标记。 [AttributeUsage(AttributeTargets.All)]public class ObsoleteAttribute: System.Attribute{ public ObsoleteAttribute(string message) {…} public string Message { get {…} } public bool IsError{ get {…} set {…} }}

评论