[C#]Attribute特性(3)——AttributeUsage特性和特性标识符

相关文章

      [C#]Attribute特性

      [C#]Attribute特性(2)——方法的特性及特性参数

AttributeUsage特性

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

1 [AttributeUsage(
2 
3 Validon,
4 
5 AllowMultiple=allowmultiple,
6 
7 Inherited=inherited
8 
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 : Attribute
11 
12     {
13         public TransactionableAttribute() { }
14     }
15 
16  [AttributeUsage(AttributeTargets.Field)]
17 public class RegistryKeyAttribute : Attribute
18 {
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 MyClass
11     {
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 MyClass
10     {
11 
12     }

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

指定继承特性的规则

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

特性标识符

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

1 public class MyClass
2 {
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 MyClass
2  {
3     [return:HRESULT]
4      public long Foo();
5 }

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402175.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 命令 ——less命令

less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大。less 的用法比起 more 更加的有弹性。在 more 的时候,我们并没有办法向前面翻, 只能往后面看,但若使用了 less …

android闹钟实现原理

闹钟的原理可用下面我自己画的一幅图来概括:(不对的地方,尽管吐槽) 我们来看看新建闹钟到闹钟响铃的步骤: 1、新建一个闹钟: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22…

将openstack的Token认证信息存储在memcache中

公司线上的openstack环境运行了5个多月的时间,keystone库的token表已经增长到了31GB,这个数据量还是很大的,对于以后的数据库备份很不方便。每次管理openstack的时候,都会产生一个新的token验证,而历史token信息其实都…

Linux 下shell编程

什么是shell?Shell是一个命令解析器,是介于Linux操作系统的内核(kernel)与用户之间的一个绝缘层。shell脚本就是讲各类命令预先放入其中,方便一次性执行的一个程序文件,主要用于方便管理员进行设置或者管理。 序员的角度来看, Sh…

linux 目录/sys 解析

今天学习Linux目录时,遇到/sys这个目录,老师怎么讲的,不太清楚,先对/sys目录知识进行一个整理 首先,对 /sys目录下的各个子目录进行具体说明: /sys下的子目录 内容 /sys/devices 该目录下…

南下事业篇——深圳 深圳(回顾)

2019独角兽企业重金招聘Python工程师标准>>> 二0一二年三月二十三号记录了下面的一篇日志,现在回味一下觉得自己有点惭愧,但不后悔,知道的越多就越了解自己的无知,工作之后渐渐磨灭了许多锐气,变得平滑低调…

php中花括号的使用

一、界定变量名 注:花括号内若左侧出现空格,则会当做普通花括号来解析。 二、界定表达式 1.获取字符串中某个字符 如:$strabcdefg; echo $str{0};//a 效果等同于$str[0]; 2.作为表示下标的方法定义数组 如:$arr []; $arr{10}4;…

游戏开发--开源软件8--cyclone2D(手机引擎+设计工具)

2019独角兽企业重金招聘Python工程师标准>>> Cyclone2D (飓风软件)是集成的手机游戏设计工具以及开源的引擎,工具提供了强大的动画、地图、数值、脚本等设计功能,开源引擎提供了一体化的模块加载与管理,并提供了详细的API文档以及…

python编码

https://www.cnblogs.com/xiao-xue-di/p/11283496.html 《Python中的Unicode编码和UTF-8编码》 《字符串和编码》 《python编码转换(unicode / utf8 / gbk / 内部编码)》 字符编码 最早127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号&#xff0…

Linux中vi显示中文乱码的问题

linux 下编程,用到的编程工具是VI,编辑编译都方便,但经常出现中文乱码问题,下面可完美解决这个问题 由于在windows下默认是gb编码,而我的vim默认是utf-8(gedit默认也是utf-8),所以打…

WIFI vs 无线网

大家好多人都在使用无线设备上网,好多人对一些名词充满了好奇,比如WLAN和WIFI的区别是什么? WIFI无线上网和WLAN无线上网是什么意思? 这篇文章中我们为大家介绍什么是WIFI无线上网?大家可能会有这样的疑问,听说最多的应该是WLAN无线上网&a…

[转]jQuery Validate使用说明

本文转自&#xff1a;http://www.cnblogs.com/gimin/p/4757064.html jQuery Validate 导入 js 库 <script src"./jquery-validation/lib/jquery-1.8.3.js" type"text/javascript"></script> <script src"./jquery-validation/dist/jqu…

Linux 设备驱动开发 —— Tasklets 机制浅析

一 、Tasklets 机制基础知识点 1、Taklets 机制概念 Tasklets 机制是linux中断处理机制中的软中断延迟机制。通常用于减少中断处理的时间&#xff0c;将本应该是在中断服务程序中完成的任务转化成软中断完成。 为了最大程度的避免中断处理时间过长而导致中断丢失&#xff0c;有…

验证码(一)

需要验证码的地方还真不少&#xff0c;这主要是为了确保用户信息的安全。这里我做了一个纯字母的验证码。Random rnew Random ();string all "";private void btnCreatAuthCode_Click(object sender, EventArgs e){GetAuthCodes();}private void GetAuthCodes(){//定…

Linux 设备驱动开发 —— platform 设备驱动

一、platform总线、设备与驱动 在Linux 2.6 的设备驱动模型中&#xff0c;关心总线、设备和驱动3个实体&#xff0c;总线将设备和驱动绑定。在系统每注册一个设备的时候&#xff0c;会寻找与之匹配的驱动&#xff1b;相反的&#xff0c;在系统每注册一个驱动的时候&#xff0c;…

HTML5本地存储——IndexedDB(二:索引)

在HTML5本地存储——IndexedDB&#xff08;一&#xff1a;基本使用&#xff09;中介绍了关于IndexedDB的基本使用方法&#xff0c;很不过瘾&#xff0c;这篇我们来看看indexedDB的杀器——索引。 熟悉数据库的同学都知道索引的一个好处就是可以迅速定位数据&#xff0c;提高搜索…

Linux 字符设备驱动开发基础(五)—— ioremap() 函数解析

一、 ioremap() 函数基础概念 几乎每一种外设都是通过读写设备上的寄存器来进行的&#xff0c;通常包括控制寄存器、状态寄存器和数据寄存器三大类&#xff0c;外设的寄存器通常被连续地编址。根据CPU体系结构的不同&#xff0c;CPU对IO端口的编址方式有两种&#xff1a; a -- …

Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析

我们在前面讲到了file_operations&#xff0c;其是一个函数指针的集合&#xff0c;用于存放我们定义的用于操作设备的函数的指针&#xff0c;如果我们不定义&#xff0c;它默认保留为NULL。其中有最重要的几个函数&#xff0c;分别是open()、read()、write()、ioctl()&#xff…

机电传动控制第一周学习笔记

机电传动控制第一周学习笔记&#xff1a; 1 这一周主要讲述了概论和机电传动控制系统动力学基础两个章节内容。 2 绪论中说明了《机电传动控制》课程主要内容为下图所示&#xff1a; 3机电传动控制系统动力学基础章节主要内容分为&#xff1a; &#xff08;1&#xff09;a&…

opengl 配置

OpenGL(Open Graphics Library)是一个跨编程语言、跨平台的专业图形程序接口。 OpenGL是SGI公司开发的一套计算机图形处理系统&#xff0c;是图形硬件的软件接口&#xff0c;任何一个OpenGL应用程序无须考虑其运行环境所在平台与操作系统&#xff0c;在任何一个遵循OpenG…