这个东西有的叫定制特性,然而我喜欢直接叫特性,但是这样的话一些人不知道我说的是什么,如果我说是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. 翻译:AKKA笔记 - 介绍Actors

    任何以前做过多线程的人都不会否认管理多线程程序是困难并且痛苦的. 我说管理是因为它开始很容易而且当你看到性能提升时会很兴奋.但是,当你看到你没法从子线程的错误中恢复 或者 这些僵尸bug很难重现 或者 ...

  2. 大叔也说Xamarin~Android篇~调用远程API接口,发POST请求

    回到目录 Xamarin我们在上节已经教大家如何去部署它的环境了,今天来说一个实际的例子,使用android客户调用.net web api的一个接口,并发送POST请求,当服务端回到请求后做出响应, ...

  3. 爱上MVC~为DisplayNameFor添加扩展,支持PagedList集合

    回到目录 DisplayNameFor方法是MVC提供给我们的,它可以将模型的DisplayName特性的值显示到页面上,这对程序员来说很是方便,在进行实体设计时,可以指定它的显示名称,然后MVC引擎 ...

  4. fir.im Weekly - 给 Mac 应用开发者的教程

    写作是一件苦差事.无论写代码,还是写文章. 关于 Mac 应用开发,国内很少有完整的书籍或教程.最近@剑指人心写的 <Mac 应用开发基础教程>终于!完!稿!了! 这本书中对 Mac 平台 ...

  5. session 学习

    session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息. 当程式需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里 ...

  6. C#与JS实现 获取指定字节长度 中英文混合字符串 的方法

    平时在作数据库插入操作时,如果用 INSERT 语句向一个varchar型字段插入内容时,有时会因为插入的内容长度超出规定的长度而报错. 尤其是插入中英文混合字符串时,SQL Server中一般中文要 ...

  7. ubuntu下在apache部署python站点

    ubuntu下在apache部署python站点 我的是ubuntu14 32为的虚拟机,默认安装的python为3.4 环境:apache + mysql + django + python3 软件 ...

  8. 13.首先,编写一个类ChongZai,该类中有3个重载的方法void print();其次, 再编写一个主类来测试ChongZai类的功能。

    package java1; //计算器 public class Jisuanqi { //属性 //型号,品牌等 //重载 //1.方法同名不同参 //2.返回类型和重载无关 //3.多态的一种表 ...

  9. 快速入门系列--NOSQL--07MongoDB

    从我第一次听到Nosql这个概念到如今已经走过4个年头了,但仍然没有具体的去做过相应的实践.最近获得一段学习休息时间,购买了Nosql技术实践一书,正在慢慢的学习.在主流观点中,Nosql大体分为4类 ...

  10. javascript中数组和字符串的方法比较

    × 目录 [1]可索引 [2]转换 [3]拼接[4]创建[5]位置 前面的话 字符串和数组有很多的相同之处,它们的方法众多,且相似度很高:但它们又有不同之处,字符串是不可变值,于是可以把其看作只读的数 ...