正文

C#属性解释 22008-08-26 01:34:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/yzrj/37869.html

分享到:

是错误的,因为它试图在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没有那样的域或属性,就会发生一个编译时错误。

·         为属性实例的运行时实例化保持下面的信息:属性类TT中的构造函数C,位置参数列表P和名称参数列表N

1.3.2 一个属性实例的运行时检索

对属性的编译产生了一个属性类TT的构造函数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 声明为一个条件方法。Class2Test方法调用这个方法。由于预定义的符号DEBUG已经被定义,因此如果Class2.Test 被调用,它就会调用M。如果符号DEBUG没有被定义,那么Class2.Test将不会调用Class1.M

注意,下面这点很重要,包含或去掉一个对条件方法的调用,是被在调用的地方的预定义标识符所控制的。在例子中

// Begin class1.cs
class 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.cs
class Class1
{
     [Conditional("DEBUG")]
     public virtual void M() {
        Console.WriteLine("Class1.M executed");
     }
}
// End class1.cs

 

// Begin class2.cs
class 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 {…} }
}

阅读(2394) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册