【C#进阶系列】18 特性Attribute
这个东西有的叫定制特性,然而我喜欢直接叫特性,但是这样的话一些人不知道我说的是什么,如果我说是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的更多相关文章
- C#进阶系列 ---- 《CLR via C#》
[C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...
- Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图
Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图 Bing Maps Silverlight Control虽然为我们提供了简洁.方面的开 ...
- C#进阶系列——DDD领域驱动设计初探(四):WCF搭建
前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...
- Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G
code&monkey Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...
- C#进阶系列——WebApi 接口参数不再困惑:传参详解
前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...
- C#进阶系列——WebApi 接口测试工具:WebApiTestClient
前言:这两天在整WebApi的服务,由于调用方是Android客户端,Android开发人员也不懂C#语法,API里面的接口也不能直接给他们看,没办法,只有整个详细一点的文档呗.由于接口个数有点多,每 ...
- C#进阶系列——WebApi 身份认证解决方案:Basic基础认证
前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...
- C#进阶系列——WebApi 异常处理解决方案
前言:上篇C#进阶系列——WebApi接口传参不再困惑:传参详解介绍了WebApi参数的传递,这篇来看看WebApi里面异常的处理.关于异常处理,作为程序员的我们肯定不陌生,记得在介绍 AOP 的时候 ...
- C#进阶系列——WebApi 路由机制剖析:你准备好了吗?
前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...
随机推荐
- 判断当前日期是否在[startDate, endDate]区间
/** * 判断当前日期是否在[startDate, endDate]区间 * * @param startDate 开始日期 * @param endDate 结束日期 * @author jqli ...
- linux下进程间通信
信号 信号是进程间相互传递消息的一种方法,只是用来通知某进程发生了什么事件,并不给进程传递任何数据. #include <sys/types.h> #include <unistd. ...
- Js~动态判断PC和手机浏览器
这个只是一个小知识,也是在网上找的,挺好用! 动态判断浏览器是PC还是移动端! <script> var browser={ versions:function(){ var u = na ...
- lua以xpcall实现try/catch功能
-- 打印错误信息 local function __TRACKBACK__(errmsg) ); print("-------------------------------------- ...
- 使用(POI)SAX处理Excel文件,防止内存溢出
POISAXReader h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-chi ...
- 每天一个linux命令(42):kill命令
Linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令.通常,终止一个前台进程可以使用Ctrl+C键,但是,对于一个后台进程就须 ...
- 每天一个linux命令(14):head 命令
head 与 tail 就像它的名字一样的浅显易懂,它是用来显示开头或结尾某个数量的文字区块,head 用来显示档案的开头至标准输出中,而 tail 想当然尔就是看档案的结尾. 1.命令格式: hea ...
- Multiton & Singleton
From J2EE Bloger http://j2eeblogger.blogspot.com/2007/10/singleton-vs-multiton-synchronization.html ...
- 关于OpenVPN的入门使用
关于OpenVPN的入门使用 1.1源代码编译安装的初步了解 1.2 安装OpenVPN 1.3 生成证书.服务器端证书.客户端证书 1.4 关于server.ovpm & client.ov ...
- sql server 链接到本地实例出错
我在使用VS2010测试package的时候,突然发现sql server 链接到本地实例出错,出错信息如下: “ A network-related or instance-specific err ...