这个东西有的叫定制特性,然而我喜欢直接叫特性,但是这样的话一些人不知道我说的是什么,如果我说是Attribute的话那么知道的或者用过的就都懂了。

还记得讲到枚举和位标志那一章,关于位标志,有一个[Flags]的用法。

    [ComVisible(true)]
[Flags]
public enum FileAttributes
{
/***/
}

这里的ComVisible和Flags就是特性。

特性的作用

利用特性可宣告式地为自己的代码构造添加注解来实现特殊功能。它相当于往元数据表里写附加信息,使得能在运行时查询到这些附加信息,从而动态改变代码的执行方式。

(实际上特性在编译后就是被序列化到元数据表中,然后获取时再反序列化为实例。)

比如下面这些例子:

  • DllImport特性可以应用于方法,告诉CLR该方法的实现位于指定DLL的非托管代码中。
  • Serializable特性可以应用于类型,告诉序列化格式器一个实例的字段可以序列化和反序列化。
  • AssemblyVersion特性可以应用于程序集,设置程序集的版本号。
  • Flags特性应用于枚举类型,枚举类型就成了位标志集合。

特性的应用

应用方式都很简单就是将特性放置在目标元素前的一对方括号中。

应用特性时,C#允许用一个前缀明确指定特性要应用于的目标元素。

 [type:SomeAttr]//应用于类型
public class People<[typevar:SomeAttr]T> {//应用于泛型
[field:SomeAttr]//应用于字段
public Int32 age; [return:SomeAttr]//应用于返回值
[method:SomeAttr]//应用于方法
public Int32 Eat(
[param:SomeAttr]//应用于参数
String foodName) {
return ;
} [property:SomeAttr]//应用于属性
public String SomeProp {
[method:SomeAttr]//应用于访问器方法
get { return null; }
} [event: SomeAttr]//应用于事件
[field: SomeAttr]//应用于编译器生成的字段
[method:SomeAttr]//应用于编译器生成的add和remove方法
public event EventHandler DieEvent; }

当然还有更上面的assembly和module分别应用于程序集和模块。

特性实际上是一个类的实例。特性类必须直接或者间接从公共抽象类System.Attribute派生。就比如[Flags]特性实际上定义它的特性类为FlagsAttribute,只不过C#允许应用特性的时候省略Attribute以简化代码。

特性的应用还有一种特殊的语法:

[DllImport("Kernel32",CharSet=CharSet.Auto,SetLastError=true)]

命名DllImportAttribute类只有一个接受一个String参数的构造器,但是上述应用中不仅提供了一个String参数,还多给了两个参数。实际上上面的语法中"Kernel32"定位参数,它是强制性的,而另外两个参数叫命名参数,它的作用是允许在构造好的特性对象中设置对象的任何公共字段和属性。

还可以将多个特性放在一个方括号中使用

[SomeAttr,AnotherAttr]

特性的定义

现在让我们去定一个特性

  [AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]//这个特性用于限定People特性只能用于类上,如果不加这个特性默认是不限制的.后面两个属性为不允许为一个元素多次指定People特性,且元素的People特性不被继承
public class PeopleAttribute:System.Attribute {//这里类名后面加后缀Attribute是为了符合微软的标准,当然也可以不加
private string _sex;
public PeopleAttribute() { }
public string Sex
{
get { return _sex; }
}
}

特性应该足够简单,因为特性实际上只是一个标识作用,记录一些类的附加信息,而不是写一些很复杂的代码到里面。

特性的字段和属性的类型也很简单,只能用基元类型,Type和枚举类型。(也可以用一维0基数组,但应该尽量避免。)

检测特性

通常特性都是和反射一起玩的,因为一般都是用反射去检测特性的存在,或者去获取特性的信息。(我记得以前自己写ORM的时候就是用反射加特性)

typeof(FileAttributes).IsDefined(typeof(FlagsAttribute), false);
//用于判断FileAttributes这个类是否应用了[Flags]特性,答案当然是true

然而这个仅仅是用于检测特性,实际上我们更多的时候是获取特性里的一些属性的信息,那么就要获取特性实例对象。

object[] arr= typeof(FileAttributes).GetCustomAttributes(typeof(FlagsAttribute), false);

上面的两个例子都是System.Reflection命名空间各个类型类(如:Assembly,MemberInfo,FieldInfo等)定义的方法,里面每个类都提供了IsDefined和GetCustomAttributes方法。

还有一个是System.Reflection.CustomAttributeExtensions这个静态类也提供了一批静态方法去检测,并且更好用。其中GetCustomAttributes直接返回Attribute[]而不是之前的Object[]。

检测特性(不创建从Attribute派生的对象)

前面那些检测方法除了IsDefined外,都会在内部调用特性类的构造器,可能还会调用属性的Get和set访问器方法。如果是首次访问类型还会调用类型构造器。

这些方法或者构造器中,如果有每次查找特性都要执行的代码,那么就会存在安全隐患。(所以说如果特性类足够简单其实不需要用到这种检测特性的)

所以有了System.Reflection.CustomAttributeData类,在查找特性时禁止执行特性类中的代码。

两个特性实例的相互匹配

System.Attribute重写了Object的Equals方法,会在内部比较两个对象的类型。不一致会返回false,一致会利用反射来比较两个特性对象中的字段值(为每个字段调用Equals)。所有字段匹配就返回true否则false。

可在自己定义的特性类中重写Equals来移除反射的使用,从而提高性能。(记得重写Equals时要重写GetHashCode)

System.Attribute还公开了虚方法Match,它的默认实现只是调用Equals并返回结果,然而我们重写它可以实现更多的匹配效果。

条件特性类

System.Diagnostics.ConditionalAttribute特性类称为条件特性类。

#define TroyTest
[Conditional("TroyTest"), Conditional("Verify")]
public class PeopleAttribute:System.Attribute {
public PeopleAttribute() { }
}

然后现在如果People特性应用到了某元素如一个类Man上,那么编译后只有当定义了TroyTest或者Verify符号的情况下,才会向Man的元数据中写入特性信息。(不过People类的定义元数据和实现还在程序集中,毕竟它是一个类,只是不向Man的元数据中写附加信息而已)

#define Test这个语法要写在文件最顶部,也就是using上方。

参考#define 用法地点:https://msdn.microsoft.com/zh-cn/library/yt3yck0x.aspx

PS:

这两天换了套博客皮肤,自己也写了部分样式,最6的是拿画图工具改了两张阿狸的图。

看了一下效果,感觉还是蛮有成就感的。

【C#进阶系列】18 特性Attribute的更多相关文章

  1. C#进阶系列 ---- 《CLR via C#》

      [C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...

  2. Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图

    Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图 Bing Maps Silverlight Control虽然为我们提供了简洁.方面的开 ...

  3. C#进阶系列——DDD领域驱动设计初探(四):WCF搭建

    前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...

  4. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  5. C#进阶系列——WebApi 接口参数不再困惑:传参详解

    前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...

  6. C#进阶系列——WebApi 接口测试工具:WebApiTestClient

    前言:这两天在整WebApi的服务,由于调用方是Android客户端,Android开发人员也不懂C#语法,API里面的接口也不能直接给他们看,没办法,只有整个详细一点的文档呗.由于接口个数有点多,每 ...

  7. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...

  8. C#进阶系列——WebApi 异常处理解决方案

    前言:上篇C#进阶系列——WebApi接口传参不再困惑:传参详解介绍了WebApi参数的传递,这篇来看看WebApi里面异常的处理.关于异常处理,作为程序员的我们肯定不陌生,记得在介绍 AOP 的时候 ...

  9. C#进阶系列——WebApi 路由机制剖析:你准备好了吗?

    前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...

随机推荐

  1. 一则线上MySql连接异常的排查过程

    Mysql作为一个常用数据库,在互联网系统应用很多.有些故障是其自身的bug,有些则不是,这里以前段时间遇到的问题举例. 问题 当时遇到的症状是这样的,我们的应用在线上测试环境,JMeter测试过程中 ...

  2. iOS-Objective-C内存管理

    内存管理: 栈区 [stack]:由编译器自动分配并释放,一般存放函数的参数值,局部变量等 堆区 [heap]:由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 全局区(静态区) ...

  3. writing-mode改变文字书写方式

    古代书写方式都是垂直方向上的,如果要实现这种效果的话,还是挺麻烦的,不过现在CSS3有一个"writing-mode"属性,它可以改变文字的书写方式. writing-mode:h ...

  4. 如何用sublime 编写sass

    使用了Sublime Text也有一段时日了,然后在现在而言,小觉的coding工具已经非其莫属了,接着小觉因为近期忙着项目的原因,同时还要抽空编辑博客的原因,就暂时把它放在一旁了,现在偶然想起也就说 ...

  5. OpenCascade BRep Format Description

    OpenCascade BRep Format Description eryar@163.com 摘要Abstract:本文结合OpenCascade的BRep格式描述文档和源程序,对BRep格式进 ...

  6. 【吐槽】VS2012的安装项目只能用InstallShield Limited Edition

    以前版本的Visual Stuido中安装项目都可以使用微软自家的Visual Studio Installer,但是到了VS2012这一切都变了,只能用InstallShield Limited E ...

  7. 哈夫曼树(一)之 C语言详解

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  8. 《Selenium2自动化测试实战--基于Python语言》 --即将面市

    发展历程: <selenium_webdriver(python)第一版>   将本博客中的这个系列整理为pdf文档,免费. <selenium_webdriver(python)第 ...

  9. javascript学习—理解addLoadEvent函数

    onload事件是HTML DOM Event 对象的一个属性,又叫事件句柄(Event Handlers),它会在页面或图像加载完成后(注意是加载完成后)立即发生. window.onload = ...

  10. RSA加密数学原理

    RSA加密数学原理 */--> *///--> *///--> UP | HOME RSA加密数学原理 Table of Contents 1 引言 2 RSA加密解密过程 2.1 ...