评论

收藏

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

编程语言 编程语言 发布于:2021-08-03 10:45 | 阅读数:270 | 评论:0

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 的事情
我们在开发中经常被数据验证所困扰,对我们来说是一个非常重复而且无聊的事情是,我们来改善一下这个事。
效果:
DSC0000.png
设计思路:

  • 使用我们的自定义特性对方法进行描述
  • 在我们的自定义特性重,实现数据验证的方法
  • 将特性判断封装为一个方法,在所有需要调用进行验证的时候,都调用此方法来进行判断如果不符合就抛出异常。
实现
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 });
    }
  }
}
效果图:
抛出我们已经定义好了的异常,以后只要是相同的判断,我们就只要对其进行标注一下调用统一的判断方法即可。
DSC0001.png

Summary
我的码云 源码下载
到此Attribute相信你已经基本上掌握了,文中有何不足之处还望指出,大家共同学习,共同进步,C#真的是一门非常优雅的语言。

除非特殊声明否则,本博客文章均属 鲁迅认识的那只猹 原创,未经许可禁止转载,否则将保留追究法律责任的权利。
如果本博客损害了您的相关权益,请及时联系我,我将妥善处理。


关注下面的标签,发现更多相似文章