C#中的特性 (Attribute) 入门 (二)
接下来我们要自己定义我们自己的特性,通过我们自己定义的特性来描述我们的代码。
自定义特性
所有的自定义特性都应该继承或者间接的继承自Attribute类。
我们在项目开发中经常要写类的创建人的注释,今天我们我们要用自定义Attribute来做这件事。
上一章,我们学到了AttributeUsage ,我们知道该特性是用描述特性的
Step 1 建立一个class 继承自Attribute
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AuthorAttribute { /// <summary> /// 用于对类的描述,该特性只能作用于类 /// </summary> [AttributeUsage(AttributeTargets.Class)] public class AuthorAttribute : Attribute { private string _author; private string _dateTime; private double _versionCode; private string _remark; /// <summary> /// 建立对类描述的attribute /// </summary> /// <param name="author">作者</param> /// <param name="dateTime">创建时间</param> /// <param name="versionCode">版本号</param> /// <param name="remark">藐视信息</param> public AuthorAttribute(string author, string dateTime, double versionCode, string remark) { this.Author = author; this.DateTime = dateTime; this.VersionCode = versionCode; this.Remark = remark; } /// <summary> /// 设置或者获取该类的创建人信息 /// </summary> public string Author { get { return _author; } private set { _author = value; } } /// <summary> /// 获取或设置类的创建时间 /// </summary> public string DateTime { get { return _dateTime; } private set { _dateTime = value; } } /// <summary> /// 类的版本号 /// </summary> public double VersionCode { get { return _versionCode; } private set { _versionCode = value; } } /// <summary> /// 获取或设置类的描述信息 /// </summary> public string Remark { get { return _remark; } private set { _remark = value; } } } }
Step 2 : 我们来使用我们上面定义好的特性
[Author("鲁迅认识的那只猹", "2017-07-08", 1.0, "建立Student类,用来存储学生的信息")] [Author("鲁迅认识的那只猹", "2017-07-08", 1.001, "为Student类添加了【DateOfBirth】属性")] public class Student { public string Name { get; set; } public string Gender { get; set; } public DateTime DateOfBirth { get; set; } }
Step 3 上面我们已经标注好了我们的类,然后我们要获取我们的描述
static void Main(string[] args) { Type type = typeof(Student); //获取到Student类的所有特性 object[] objs = type.GetCustomAttributes(true); //循环判断我们我们获取到的objs数组 foreach (object item in objs) { AuthorAttribute attr = item as AuthorAttribute; if (attr != null) Console.WriteLine ( "Author:" + attr.Author + " Version Code:" + attr.VersionCode + " Date Time:" + attr.DateTime + " Remark:" + attr.Remark ); } Console.ReadKey(); } /*输出结果 Author:鲁迅认识的那只猹 Version Code:1 Date Time:2017-07-08 Remark:建立Student类,用来存储学生的信息 Author:鲁迅认识的那只猹 Version Code:1.001 Date Time:2017-07-08 Remark:为Student类添加了【DateOfBirth】属性 */
到此我们应该已经对自定义特性有了一个简单的了解,下面我们将用自定义Attribute来做一件很cool的事情。
Cool 的事情
我们在开发中经常被数据验证所困扰,对我们来说是一个非常重复而且无聊的事情是,我们来改善一下这个事。
效果:
设计思路:
- 使用我们的自定义特性对方法进行描述
- 在我们的自定义特性重,实现数据验证的方法
- 将特性判断封装为一个方法,在所有需要调用进行验证的时候,都调用此方法来进行判断如果不符合就抛出异常。
实现
Step 1: 建立一个用来判断参数的父类,所有的用于验证的Attribute都将继承自此类。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataValidateAttribute { /// <summary> /// 所有验证类特性的父类,所有的用于验证的特性都将继承此类 /// </summary> /* AttributeTargets.Parameter 标识该特性作用于 方法的参数 Inherited = true 标识该特性可以被继承 AllowMultiple = true 标识可以多次标注 */ [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = true)] public abstract class ValidateAttribute : Attribute { public ValidateAttribute() { } public ValidateAttribute(string msg) { this.Message = msg; } /// <summary> /// 被验证的参数名称 /// </summary> private string _argumentName; /// <summary> /// 抛出错误的信息 /// </summary> private string _message; /// <summary> /// 获取被验证的参数名称 /// </summary> public string ArgumentName { set { _argumentName = value; } protected get { return _argumentName; } } /// <summary> /// 异常的提示信息 /// </summary> public string Message { protected get { return _message; } set { _message = value; } } /// <summary> /// 验证该值是否符合指定的规则 /// </summary> /// <param name="value"></param> /// <returns></returns> public abstract void IsValidation(object value); } }
Step 2: 实现我们的验证用的子类
[NotNullAttribute]
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataValidateAttribute { public class NotNullAttribute : ValidateAttribute { public NotNullAttribute() { } public NotNullAttribute(string msg) : base(msg) { } public override void IsValidation(object value) { if (value == null) { throw new ArgumentNullException(this.ArgumentName + " " + Message); } } } }
[ValidationAgeAttribute]
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataValidateAttribute { public class ValidationAgeAttribute : ValidateAttribute { public ValidationAgeAttribute() { } public ValidationAgeAttribute(string msg) : base(msg) { } public override void IsValidation(object value) { int age = Convert.ToInt32(value); if (age <= 0) { throw new ArgumentException(this.ArgumentName + " " + this.Message); } } } }
Step 3 抽取一个统一使用的用于验证的方法:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace DataValidateAttribute { public static class ValidateContext { /// <summary> /// 验证方法的参数是否合法 /// </summary> /// <param name="values">被判断的值,值得顺序必须按照参数特性的顺序来传值</param> public static void Validate(object[] values) { //从方法栈中拿到刚执行的方法 MethodInfo method = (MethodInfo)(new StackTrace().GetFrame(1).GetMethod()); //获取到方法的参数 ParameterInfo[] parameterInfos = method.GetParameters(); if (parameterInfos.Length == 0) return; int index = 0; //遍历所有的参数 foreach (var item in parameterInfos) { //获取被标记的特性的数组 ValidateAttribute[] attributes = (ValidateAttribute[])Attribute.GetCustomAttributes(item, typeof(ValidateAttribute)); if (attributes != null) { foreach (var attr in attributes) { //如果没有异常就证明验证通过 try { attr.ArgumentName = item.Name; attr.IsValidation(values[index]); } //如果有异常那么就表示验证没有通过,抛出我们指定的异常 catch (Exception e) { throw e; } } index += 1; } } } } }
Step 5 检验效果:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataValidateAttribute { class Program { static void Main(string[] args) { Console.ReadKey(); } /// <summary> /// 普通方式来验证参数的方法 /// </summary> /// <param name="name"></param> /// <param name="gender"></param> /// <param name="age"></param> static void Dome1(string name, string gender, int age) { if (name == null) { throw new ArgumentNullException("name"); } if (gender == null) { throw new ArgumentNullException("gender"); } if (age <= 0) { throw new ArgumentException("age"); } } /// <summary> /// 使用特性来验证参数的方法 /// </summary> /// <param name="name"></param> /// <param name="gender"></param> /// <param name="age"></param> static void Demo2([NotNull("名字你还想空?")]string name, [NotNull]string gender, [ValidationAge("年龄错误 不能小于0")]int age) { ValidateContext.Validate(new object[] { name, gender, age }); } } }
效果图:
抛出我们已经定义好了的异常,以后只要是相同的判断,我们就只要对其进行标注一下调用统一的判断方法即可。
Summary
到此Attribute相信你已经基本上掌握了,文中有何不足之处还望指出,大家共同学习,共同进步,C#真的是一门非常优雅的语言。
除非特殊声明否则,本博客文章均属 鲁迅认识的那只猹 原创,未经许可禁止转载,否则将保留追究法律责任的权利。
如果本博客损害了您的相关权益,请及时联系我,我将妥善处理。