博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[C#]Attribute特性(3)——AttributeUsage特性和特性标识符
阅读量:6246 次
发布时间:2019-06-22

本文共 5254 字,大约阅读时间需要 17 分钟。

相关文章

     

      

AttributeUsage特性

     除了可以定制自己的特性来注释常用的C#类型外,您可以用AttributeUsage特性来定义您想怎样使用这些特性。AttributeUsage特性采用如下的调用惯例:

1 [AttributeUsage(2 3 Validon,4 5 AllowMultiple=allowmultiple,6 7 Inherited=inherited8 9 )]

      您可以非常容易地区别出哪些是定位参数,哪些是命名参数。强烈建议您在记录您的特性时采用这种格式,以便不必通过查看特性类的源代码,您的用户即可以找到哪些公共的读/写字段和属性可以用作命名特性。

定义一个特性目标

     现在,让我们再来看看AttributeUsage特性,您会注意到validon参数是一个定位的(必需的)参数。这个参数使您的特性都能附加给哪些类型。确切地说,AttributeUsage特性中的validon参数是AttributeTargets类型的,它事实上是按如下方式定义的一种枚举:

1 Public enum AttributeTargets  2   3 {  4   5       // 摘要:  6   7         //     可以对程序集应用属性。  8   9         Assembly = 0x0001, 10  11         // 12  13         // 摘要: 14  15         //     可以对模块应用属性。 16  17         Module = 0x0002, 18  19         // 20  21         // 摘要: 22  23         //     可以对类应用属性。 24  25         Class = 0x0004, 26  27         // 28  29         // 摘要: 30  31         //     可以对结构应用属性,即值类型。 32  33         Struct = 0x0008, 34  35         // 36  37         // 摘要: 38  39         //     可以对枚举应用属性。 40  41         Enum = 0x0010, 42  43         // 44  45         // 摘要: 46  47         //     可以对构造函数应用属性。 48  49         Constructor = 0x0020, 50  51         // 52  53         // 摘要: 54  55         //     可以对方法应用属性。 56  57         Method = 0x0040, 58  59         // 60  61         // 摘要: 62  63         //     可以对属性 (Property) 应用属性 (Attribute)。 64  65         Property = 0x0080, 66  67         // 68  69         // 摘要: 70  71         //     可以对字段应用属性。 72  73         Field =0x0100, 74  75         // 76  77         // 摘要: 78  79         //     可以对事件应用属性。 80  81         Event = 0x0200, 82  83         // 84  85         // 摘要: 86  87         //     可以对接口应用属性。 88  89         Interface = 0x0400, 90  91         // 92  93         // 摘要: 94  95         //     可以对参数应用属性。 96  97         Parameter = 0x0800, 98  99         //100 101         // 摘要:102 103         //     可以对委托应用属性。104 105         Delegate = 0x1000,106 107         // 摘要:108 109         //     可以对任何应用程序元素应用属性。110 111         All = Assembly|Module|Class|Struct|Enum|Constructor|Method|Property|Field|Event|Interface|Parameter|Delegate,112 113 ClassMember=Class|Struct|Enum|Constructor|Method|Property|Field|Event|Delegate|Interface,114 115 }
AttributeTargets

     注意当使用AttributeUsage特性时您可以指定AttributeTargets.All,这样的话,这个特性就可以附加给任何在AttributeTargets枚举中列出的类型了。这是在您不指定AttributeTargets特性时的默认方式。假如AttributeTargets.All是默认值,您可能会觉得奇怪:为什么您还要使用validon值。其原因就是命名参数可以在这个特性上使用,您也许要改变他们其中的一个。但是您要记住:如果您使用了一个命名参数,您必须把它放在所有的定位参数的后面。这可以让您很方便地指定您想使用的特性采用AttributeTargets.All默认值,同时您仍然可以设置它们的命名参数。

     那么,在什么时候并且为什么您指定这个validonAttributeTargets)参数?在任何您想完全控制怎样使用一个特性时,您都可以使用这个参数。在上面的这些例子中,我们创建了一个只有类才能使用的RemoteObjectAttribute特性,还创建了一个只能用在方法上的TransactionableAttribute特性,以及一个只对字段有作用的RegistryKeyAttribute特性。如果我们想让这些特性只注释在设计时它们所注释的那些类型,我们可以这样来定义它们(此处为简明起见略去了特性的主体):

1 [AttributeUsage(AttributeTargets.Class)] 2 public class RemoteObjectAttribute:Attribute 3  4 { 5  6     ...... 7  8 } 9  [AttributeUsage(AttributeTargets.Method)]10  public class TransactionableAttribute : Attribute11 12     {13         public TransactionableAttribute() { }14     }15 16  [AttributeUsage(AttributeTargets.Field)]17 public class RegistryKeyAttribute : Attribute18 {19         .........20 }

     最后关于AttributeTargets枚举有一点要提一下:您可以使用“|”操作符来组合成员。如果您有一个既要应用到字段和又要应用到属性的特性,您就可以按如下格式来附加这个AttributeUsage特性:

1 [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]

单次特性和多次特性

      您可以用AttributeUsage来把特性定义为单次或者是多次的特性。使用哪种形式取决于一个单的特性在一段单独的字段里的使用次数。在默认形式下,所有的特性都是单次的,这以为着在编译如下代码时将导致一个编译错误:

1 public class SigleUseAttribute : Attribute 2 { 3         public SigleUseAttribute(string str) 4         { } 5 } 6  7 //Error:This results in a “duplicate attribute ” complier error. 8     [SigleUse("abc")] 9     [SigleUse("def")]10     public class MyClass11     {12             ......13     }

      为了解决这个问题,您需要在AttributeUsage那一行指定您要把这个特性多次附加给某些类型。具体方法如下:

1 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] 2 public class SigleUseAttribute : Attribute 3  { 4     public SigleUseAttribute(string str) 5         { } 6  } 7     [SigleUse("abc")] 8     [SigleUse("def")] 9     public class MyClass10     {11 12     }

      您可能用到这种方法的一个实际例子就是用在“特性的定义”一节中所讨论到的RegistryKeyAttribute特性上。因为我们知道一个字段可能会存储在注册表的多个地方,您可能需要(就像这里的代码一样)通过AllowMultiple 命名参数把AttributeUsage特性附加给它们。

指定继承特性的规则

      AttributeUsageAttribute特性的最后一个参数就是他的inherited标记,即用来标识这个特性是否可以继承。它的默认值是false。但是,如果inherited标记被设置成true,那么它还需要看AllowMultiple 标记的值。如果inherited标记为trueAllowMultiple 标记为false,这个特性就不再有继承特性了。不过如果标记是true并且AllowMultiple 标记也是true,这个特性就可以累加到这个成员上。

特性标识符

      请看一下下面的代码,并试着标出这个特性是否注释了其中的返回值或方法:

1 public class MyClass2 {3         [HRESULT]4         public long Foo();5 }

      如果您具有COM编程经验的话,您就会知道HRESULT是所有错了名为AddRefRelease的方法的基本返回类型。不过,我们可以很容易就明白,如果特性的名字既可以用于返回值又可以用于方法名的或,编译器就不可能知道您的用意到底是什么。下面是编译器不能根据上下文掌握您的真正用意的其他一些情况:

  • 方法和返回类型
  •  事件、字段和属性
  • 委托和返回类型
  • 属性、存取器、getter方法的返回值和setter方法的值参数

      在这些情况下,编译器根据它所认为的最可能的原则来进行判断。为了不让这种判断出现,您可以使用下面所列出的特性标识符:

  • assembly
  • module
  • type
  • method
  • property
  • event
  • field
  • param
  • return

       要使用特性标识符,只需在这个特性名字前面加上所需的标识符和一个冒号即可。在MyClass例子中,如果您想保证编译器能够判断出HRESULT是用来注释返回值而不是其中的方法,您就可以这样指定标识符:

1 public class MyClass2  {3     [return:HRESULT]4      public long Foo();5 }

总结

      C#特性提供了这样一种机制,就是在设计时可以用信息来对类型和成员进行注释,并且可以在运行时通过反射来获取这些信息。这使您可以真正创建自我包含的、自我描述的组件,而不必借助于向一些资源文件和常量中填塞一些必须的琐碎信息。这样有利于编出更简单、更易于维护、移植性更强的组件。

     本文来自《c#技术内幕》,记录在次,方便自己,方便他人.....

转载地址:http://szria.baihongyu.com/

你可能感兴趣的文章
【java设计模式】之 单例(Singleton)模式
查看>>
Linux_自制系统服务启动脚本
查看>>
干货:解码OneData,传说中的阿里数据中台是如何练成的?
查看>>
gorename: easy refactoring tool for Golang[转]
查看>>
数据库进程间通信解决方案
查看>>
Guice框架-DI(依赖注入之作用域)
查看>>
海量数据计算应该如何选择数据库
查看>>
CodeCounter
查看>>
MongoDB 如何保证 oplog 顺序?
查看>>
TF-IDF与余弦相似性的应用(一):自动提取关键词
查看>>
冒泡排序、插入排序、选择排序、快速排序、二分查找(Objective-C实现)
查看>>
nginx反向代理入门
查看>>
Ionic 开发中遇到的常见问题及解决方案
查看>>
mysql数据库存储路径更改 数据文件位置
查看>>
ExtJS5学习之MVVC
查看>>
PDF文档压缩
查看>>
[J2ME]手机流媒体之实作[附源码][与RTSP/MMS协议无关]
查看>>
Scala程序设计读书笔记[1:5]
查看>>
概率论快速学习05:随机变量 二项分布 泊松分布
查看>>
构建Tiny生态圈
查看>>