.NET内置特性(二)——自定义特性+反射查看
来源:互联网 发布:php抽象类和接口的作用 编辑:程序博客网 时间:2024/06/11 21:49
前言
上一篇简单的介绍了一下Attribute的基础概念以及应用场景和方法,这一篇文章,我们就来聊聊如何自定义一个特性
实例
需求:
在创建或者更新一个类文件时,需要说明这个类是什么时候由谁创建的,在以后的更新中还要说明什么时候偶是由谁更新的,可以记录也可以不记录更新的内容,以往我们可以通过注释的方式在类上边添加注释:
//更新:Celine,2017年5月29日,修改了ToString()方法 public class DemoClass { public override string ToString() { return "This is a demo class"; } }
但是如果有一天想要查看所有类型的更新记录怎么办呢?是不是一个一个的去查看源文件,找出这些注释?
咱们再来回顾一下特性的定义:特性可以用于给类型添加元数据,这些元数据用于描述类型。那么我们是不是可以将这些描述信息通过特性来为类进行添加,在这个例子中,要附加的类型元素应该是:注释类型(“更新”或者“创建”)、修改人、日期、备注信息(可有可无)
1.先创建一个封装了元数据的类RecordAttribute:
public class RecordAttribute:Attribute { private string recordType; //记录类型:更新/创建 private string author; //作者 private DateTime date; //更新/创建日期 private string memo; //备注 //构造函数,构造函数的参数在特性中也成为“位置参数” public RecordAttribute(string recordType,string author,string date){ //特性类的构造函数的参数有一些限制:必须为敞亮、Type类型,或者是常量数组 //因此不能直接传递DateTime类型,只能传递String类型,然后在构造函数内进行一个强制类型转换 this.recordType=recordType; this.author = author; this.date = Convert.ToDateTime(date); } //对于位置参数,通常只提供Get访问器 public string RecordType { get { return recordType; } } public string Author { get{return author;} } public DateTime Date { get{return date;}} public string Memo { get { return memo; } } //构建一个属性,在特性中也叫“命名参数” public string Memo { get; set; } }
这样定义是不够的,因为上看去就是一个普通的类没有任何区别,我们先看一下上一篇中用到的预定义特性: Obsolete是如何写的:
// 摘要: // 标记不再使用的程序元素。 此类不能被继承。 [Serializable] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ComVisible(true)] public sealed class ObsoleteAttribute : Attribute { public ObsoleteAttribute(); public ObsoleteAttribute(string message); ol error); public bool IsError { get; } public string Message { get; } }
注意一下几个特点:
- 继承自Attribute类
- 在Obsolete上边有用了三个特性去描述他
- Serializable:支持序列化
- AttributeUsage:帮助我们控制定制特性的使用
- ComVisible :对COM的可访问性
他们是描述元数据的特性,所以可以成为元元数据(meta-metadata)
如果是自定义的特性,我们只需要用到AttributeUsage这一个特性就可以了,那么,我们就来看看AttributeUsage是如何定义的:
// 摘要: // 指定另一特性类的用法。 此类不能被继承。 [Serializable] [AttributeUsage(AttributeTargets.Class, Inherited = true)] [ComVisible(true)] public sealed class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn); public bool AllowMultiple { get; set; } public bool Inherited { get; set; } public AttributeTargets ValidOn { get; } }
特点:
- 有一个构造函数,这个构造函数含有一个AttributeTargets的位置参数
- 有两个命名参数(AllowMultiple、Inherited)
根据特性的书写规范,必须写成一行,位于所应用的目标类型上,就会采用一种特殊的写法:不管是构造函数的参数还是属性,全部写到构造函数的圆括号中,对于构造函数的参数,必须采取构造函数的参数的顺序和类型,因此叫做位置参数;对于属性,采用“属性”=“值”的格式,他们之间用逗号分隔,称作命名参数
我们的RecordAttribute如果写好了,那么在使用的时候就应该是如下所示
[Record("更新", "Celine", "2017年5月29日", Memo="修改ToString(方法)")]public class DemoClass{}
其中的recordType,author和date是位置参数,Memo是命名参数
在AttributeUsage中的构造函数接受一个AttributeTargets类型的参数,而这个AttributeTargets是一个位标记,他表示这个特性可以加载在哪些类型上,如果写成AttributeTargets.Class,代表可以应用于类这个类型上
// 指定可以对它们应用特性的应用程序元素。 [Serializable] [ComVisible(true)] [Flags] public enum AttributeTargets { Assembly = 1, Module = 2, Class = 4, Struct = 8, Enum = 16, Constructor = 32, Method = 64, Property = 128, Field = 256, Interface = 1024, Delegate = 4096, ReturnValue = 8192, GenericParameter = 16384, All = 32767, }
AllowMuitple:设置该特性是不是可以重复地添加到一个类型上,例如
[Record("更新", "Celine", "2017年5月29日", Memo="修改ToString(方法)")] [Record("更新", "Celine", "2017年5月28日")] [Record("创建", "Celine", "2017年5月28日")] public class DemoClass { public override string ToString() { return "This is a demo class"; } }
Inherited:是否能够被继承
2.实现RecordAttribute
只需要使用AttributeUsage来标注这个类就可以了,主体代码不需要改变
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple=true,Inherited=false)]
[Record("更新", "Celine", "2017年5月29日", Memo="修改ToString(方法)")] [Record("更新", "Celine", "2017年5月28日")] [Record("创建", "Celine", "2017年5月28日")] public class DemoClass { public override string ToString() { return "This is a demo class"; } }
static void Main(string[] args) { DemoClass demo = new DemoClass(); Console.WriteLine(demo.ToString()); Console.ReadLine(); } }
3.使用反射来查看元数据
Type t = typeof(DemoClass); Console.WriteLine("下面列出应用于{0}的RecordAttribute属性:", t); //获取所有的ReconrdAttribute特性 object[] records = t.GetCustomAttributes(typeof(RecordAttribute), false); foreach (RecordAttribute record in records) { Console.WriteLine(" {0}",record); Console.WriteLine(" 类型:{0}",record.RecordType); Console.WriteLine(" 作者:{0}",record.Author); Console.WriteLine(" 日期:{0}",record.Date.ToShortDateString()); if (!String.IsNullOrEmpty(record.Memo)) { Console.WriteLine(" 备注:{0}",record.Memo); } } Console.ReadLine();
总结
这两篇博客介绍了.NET的两种很重要的应用:特性+反射,特性说的够多了,现在一句话说说反射,反射提供这样几个能力:查看和遍历类型和类型成员的元数据;动态创建类型实例,动态调用所创建的势力的方法,字段,属性(机房中的反射,动态的创建一个接口);迟绑定方法和属性,这里用到的第一个功能,以后有机会在研究别的功能。
- .NET内置特性(二)——自定义特性+反射查看
- c# 特性/属性(Attribute) 以及使用反射查看自定义特性
- c# 特性/属性(Attribute) 以及使用反射查看自定义特性
- .NET内置特性(一)
- .Net——自定义特性(Custom Attributes)的创建与查看
- .net 特性 反射 实例
- .NET特性与反射
- .Net 中的反射(反射特性)
- .Net 中的反射(反射特性)
- Attribute(二)——自定义特性+Asp.net MVC中的filter详解
- Attribute(二)——自定义特性+Asp.net MVC中的filter详解
- .Net中的自定义特性
- C#自定义特性和反射
- 浅析.NET的反射特性
- 浅析.NET的反射特性
- 浅析.NET的反射特性
- .Net 中的反射(反射特性) - Part.3
- .Net 中的反射(反射特性) - Part.3
- CDN
- android studio日志打印神器,日志代码跟踪器ELog
- 大数据存储系统(4)--- 图存储系统(Graph Database)
- linux ssh 登录管理
- 请编写一个方法,将字符串中的空格全部替换为“ ”。假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实长度(小于等于1000),同时保证字符串由大小写的英文字母组成。
- .NET内置特性(二)——自定义特性+反射查看
- (转)ARM协处理器CP15寄存器详解
- jz2440烧写开发板uboot,内核和文件系统等的相关命令
- Unity Shader入门精要学习笔记
- LeetCode 375. Guess Number Higher or Lower II
- Boolan STL与泛型编程 第二周笔记
- 1.1 渲染管线概述
- 山东省第八届ACM省赛G和J
- 十分钟搞定pandas