科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件在Visual C sharp中定义和使用自己的特性(3)

在Visual C sharp中定义和使用自己的特性(3)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

自定义特性有点类似于XML,它最大的好处不在于“它做了什么”,它真正最大的好处在于“你可以用它做什么”。这个是真正无止境的,由于自定义特性本身具有开放的特性,这使得它可以拥有更多新颖的用途。

作者:David Tansey 来源:天极网 2007年8月31日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

如果你之前没有接触过特性,那么你将对下面的代码有点陌生。

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple = true)]

这一行代码,把特性[AttributeUsage]附加到特性类的定义上。方括号的语法表明一个特性的构造器被调用。所以,特性类也可以拥有它们自己的特性,这看起来可能有点混淆,但是随着我给你展示可以用特性类来做些什么,你对它的认识,将会越来越清晰。

[AttributeUsage]特性具有一个定位参数和两个命名参数。定位参数指定了特性类将被用于何种类型,定位参数的值是枚举AttributeTargets的组合。在我的例子里,我仅仅把特性类应用在类和方法上,所以通过组合两个AttributeTargets的值的满足了我的要求。

[AttributeUsage]特性的第一个命名参数是AllowMultiple,该参数指定了是否可以在同一个类型上应用多次(你所定义的)特性类。默认值是false,即不允许应用多次。但是,根据这个例子的实际情况,你将会在某一类型上不止一次的应用特性(DefectTrackAttribute),所以应该使用[AttributeUsage]的命名参数AllowMultiple,并将其设置为true。这是因为,一个特定的类和方法在其生命周期里会经历多次修订,所以你需要使用[DefectTrackAttribute]特性记录每一次变化。

[AttributeUsage]特性的第二个命名参数是Inherited,它指定了派生类(使用此特性类的子类)是否继承此特性。我使用了此参数的默认的值false。因为我使用的是默认值,所以也就不需要指定该命名参数。为什么不需要继承呢?我想获取源代码的修改信息是跟每一个具体的类和方法有关的。如果把Inherited设为true,那么开发者将会混淆一个类的[DefectTrackAttribute]特性,无法辨别[DefectTrackAttribute]特性是它自己的还是从父类继承的。

上面的代码展示了特性类(DefectTrackAttribute)的定义。它继承于System.Attribute,事实上,所有的特性均直接或间接的继承于System.Attribute。

上面的代码里,还定义了特性的5个私有的字段,这些字段均用于保存与特性相关的值。

在我们特性类中第一个方法是构造器,它是带有3个参数的签名。构造器的参数对于特性类而言,就是这个特性的定位参数,这些参数是强制性的。如果你愿意,你可以重载构造器,使其可以拥有更多的有关定位参数配置的选择。

我们的特性类中剩下的部分就是一些公有属性的声明,这些属性与类中的私有字段相对应。当你查阅元数据的时候,你可以使用这些属性访问该特性的值。需要说明的是,对应定位参数的属性没有set语句,只有get语句。这就导致了这些属性是只读的,这也与它们是定位参数而不是命名参数的含义相一致。

应用自定义特性

你现在已经知道在C#代码里,在一个类型声明之前,通过在方括号里使用特性的名字和参数就可以将其附加到目标类型上。

在下面的代码里,把[DefectTrack]特性附加到一对类和一对方法上。

using System ;

using MyAttributeClasses ;

namespace SomeClassesToTest

{

[DefectTrack( "1377", "12/15/02", "David Tansey" ) ]

[DefectTrack( "1363", "12/12/02", "Toni Feltman",

Origin = "Coding: Unhandled Exception" ) ]

public class SomeCustomPricingClass

{

public double GetAdjustedPrice(

double tnPrice,

double tnPctAdjust )

{ return tnPrice + ( tnPrice * tnPctAdjust ) ; }

[DefectTrack( "1351", "12/10/02", "David Tansey",

Origin = "Specification: Missing Requirement",

FixComment = "Added PriceIsValid( ) function" ) ]

public bool PriceIsValid( double tnPrice )

{ return tnPrice > 0.00 && tnPrice < 1000.00 ; }

}

[DefectTrack( "NEW", "12/12/02", "Mike Feltman" ) ]

public class AnotherCustomClass

{

string cMyMessageString ;

public AnotherCustomClass( ){ }

[DefectTrack( "1399", "12/17/02", "David Tansey",

Origin = "Analysis: Missing Requirement" ) ]

public void SetMessage( string lcMessageString )

{ this.cMyMessageString = lcMessageString ; }

}

}

首先,需要确保你可以访问之前创建的自定义特性,所以需要添加这样一行代码,如下:

using MyAttributeClasses ;

到此,你就可以使用自定义特性[DefectTrack]装饰或点缀你的类声明和方法了。

SomeCustomPricingClass有两处地方用到了[DefectTrack]特性。第一个[DefectTrack]特性仅仅使用了三个定位参数,而第二个[DefectTrack]特性还包含了一个命名参数Origin的指定。

[DefectTrack( "1377", "12/15/02", "David Tansey" ) ]

[DefectTrack( "1363", "12/12/02", "Toni Feltman",

Origin = "Coding: Unhandled Exception" ) ]

public class SomeCustomPricingClass

{}

PriceIsValid()方法也使用了自定义特性[DefectTrack],并且指定了两个命名参数Origin和FixComment。上述代码包含了[DefectTrack]特性几个额外的用途,你可以检测这些特性。

一些读者可能会感到惊奇,因为对于源代码修改的信息可以通过使用注释这种传统的做法。.NET已经使用工具,通过在注释里使用XML块,把这些信息很好的组织起来。

在源代码对应的位置,你可以很容易的看到你的注释。你可以通过文本,分析源代码里的注释,从而处理这些信息,但是这个过程是单调冗长的,并且很容易出现错误。.NET提供了工具来处理注释里的XML块,这样可以消除此类问题。

使用自定义特性可以使你达到同样的效果,它同样提供了一种可以有效组织的方法,用于记录和处理这些信息,并且它还有一个额外的优势。考虑如下情况,当把源代码编译成二进制代码的时候,你是否已经丢失了代码的注释?毫无疑问,注释已经作为副产品,永远的从可执行代码里移出。相比之下,特性的值已经变成了元数据的一部分,永远的绑定到一个程序集里。在没有源代码的情况下,你依然可以访问这些注释信息。

另外,在源代码里允许特性构造一个与当初在设计时值一样的实例。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章