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#真的是一门非常优雅的语言。