C#中的特性 (Attribute) 入门 (二)

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

posted @ 2017-07-08 11:33  鲁迅认识的那只猹  阅读(337)  评论(0编辑  收藏  举报