本文以一个项目中通用的验证类来举例说明如何使用自定义Attribute来扩展元数据。

 在项目中,我们为了保证各个层次之间的松藕合,通常把在各个层次之间传递数据的封装在一个称为实体类的类中,比如ActionFrom

  1. using System;
  2. namespace AttributeTest
  3. {
  4. public class ActionForm
  5. {
  6. private string email = "";
  7. private string password = "";
  8. public string Email
  9. {
  10. get { return this.email; }
  11. set { this.email = value; }
  12. }
  13. public string Password
  14. {
  15. get { return this.password; }
  16. set { this.password = value; }
  17. }
  18. }
  19. }

现在,在使用这些实体类中的数据之前,我们需要对其中的数据进行验证。通常我们会写个静态类,用来提供各种不同的验证方法。比如需要验证Email,验证Password,比如:

  1. using System;
  2. using System.Reflection;
  3. using System.Text.RegularExpressions;
  4. namespace AttributeTest
  5. {
  6. public class Validator
  7. {
  8. public static bool ValidateEmail(string email)
  9. {
  10. //方法体
  11. }
  12. public static bool ValidatePassword(string passwd)
  13. {
  14. //方法体
  15. }
  16. }
  17. }

这样的硬编码混迹于各个层次之间,一旦实体类里某个属性发生变化,就不得不修改各个层次中的相关验证代码。于是,我们想到可以使用一个统一的验证方法用来验证所有的实体类中的属性。

  1. public static bool Validate(string propertyName, string propertyValue, Validator.ValidateType t) {...}

这里,Validator.ValidateType 是Validator中提供的一个枚举。

  1. public enum ValidateType
  2. {
  3. Email,
  4. Password,
  5. Number,
  6. Id
  7. }

这里这个验证方法,的第三个参数使得验证与实体类的耦合密度增加了。我们还是不得不在修改实体类的时候,修改验证方法的调用代码。

现在,我们需要自定义Attribute来扩展实体类的元数据。通过对实体类元数据的描述,我们可以去掉验证方法里的第三个参数

  1. using System;
  2. namespace AttributeTest
  3. {
  4. [System.AttributeUsage(AttributeTargets.Property)]
  5. public class ValidateAttribute : System.Attribute
  6. {
  7. public ValidateAttribute(ValidateType validateType)
  8. {
  9. this.validateType = validateType;
  10. }
  11. private ValidateType validateType;
  12. public ValidateType ValidateType
  13. {
  14. get { return this.validateType; }
  15. set { this.validateType = value; }
  16. }
  17. }
  18. public enum ValidateType
  19. {
  20. Email,
  21. Password,
  22. Number,
  23. Id
  24. }
  25. }

自定义Attribute(特性)必须继承于System.Attribute。还可以通过System.AttributeUsageAttribute特性,控制自定义特性的使用范围(构件),例如,字段、方法。[System.AttributeUsage(AttributeTargets.Property)]限制这个自定义特性只能使用在类的属性上。

现在,我们实现这个验证方法:

  1. using System;
  2. using System.Reflection;
  3. using System.Text.RegularExpressions;
  4. namespace AttributeTest
  5. {
  6. public class Validator
  7. {
  8. public static bool Validate(object validateObject, string validateProperty)
  9. {
  10. System.Type t = validateObject.GetType();
  11. PropertyInfo pi = t.GetProperty(validateProperty);
  12. string validateValue = pi.GetValue(validateObject, null) as string;
  13. if (pi.IsDefined(typeof(ValidateAttribute), true))
  14. {
  15. object[] atts = pi.GetCustomAttributes(true);
  16. ValidateAttribute vatt = atts[0] as ValidateAttribute;
  17. string strExpr = "";
  18. switch (vatt.ValidateType)
  19. {
  20. case ValidateType.Email:
  21. strExpr = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+{1}quot;;
  22. break;
  23. case ValidateType.Password:
  24. strExpr = @"\d{6}";
  25. break;
  26. case ValidateType.Number:
  27. strExpr = @"^\d*{1}quot;;
  28. break;
  29. case ValidateType.Id:
  30. strExpr = @"^\w*{1}quot;;
  31. break;
  32. default:
  33. return true;
  34. }
  35. Regex validateRegex = new Regex(strExpr);
  36. return validateRegex.IsMatch(validateValue);
  37. }
  38. return true;
  39. }
  40. }
  41. }

该方法需要两个参数,一个是需要验证的实体类的实例,还有一个是需要验证的属性名。当然,我们还需要在实体类上加上我们自定义的特性:

  1. using System;
  2. namespace AttributeTest
  3. {
  4. public class ActionForm
  5. {
  6. private string email = "";
  7. private string password = "";
  8. [Validate(ValidateType.Email)]
  9. public string Email
  10. {
  11. get { return this.email; }
  12. set { this.email = value; }
  13. }
  14. [Validate(ValidateType.Password)]
  15. public string Password
  16. {
  17. get { return this.password; }
  18. set { this.password = value; }
  19. }
  20. }
  21. }

我们通过自定义特性对实体类的元数据进行扩展,指定每个属性需要验证的类型。

现在我们可以这样使用这个验证类:

  1. ActionForm form = new ActionForm();
  2. form.Email = justacoder@123.com;
  3. form.Password = "123456";
  4. bool isValidEmail = Validator.Validate(form, "Email");
  5. bool isValidPassword = Validator.Validate(form, "Password");
  6. Console.WriteLine("Email is {0}.", isValidEmail?"valid":"invalid");
  7. Console.WriteLine("Password is {0}.", isValidPassword?"valid":"invalid");
  8. Console.ReadLine();

我们通过抛出自定义异常的方法,将验证扩大到实体类级别的验证:

  1. public static void ValidateProperty(object validateObject, string validateProperty)
  2. {
  3. System.Type t = validateObject.GetType();
  4. PropertyInfo pi = t.GetProperty(validateProperty);
  5. string validateValue = pi.GetValue(validateObject, null) as string;
  6. if( pi.IsDefined(typeof(ValidateAttribute), true) )
  7. {
  8. object[] atts = pi.GetCustomAttributes(true);
  9. ValidateAttribute vatt = atts[0] as ValidateAttribute;
  10. string strExpr = "";
  11. switch(vatt.ValidateType)
  12. {
  13. case ValidateType.Email:
  14. strExpr = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+{1}quot;;
  15. break;
  16. case ValidateType.Password:
  17. strExpr = @"\d{6}";
  18. break;
  19. case ValidateType.Number:
  20. strExpr = @"^\d*{1}quot;;
  21. break;
  22. case ValidateType.Id:
  23. strExpr = @"^\w*{1}quot;;
  24. break;
  25. default:
  26. return;
  27. }
  28. Regex validateRegex = new Regex(strExpr);
  29. if( !validateRegex.IsMatch(validateValue) )
  30. {
  31. throw new ApplicationException(validateProperty + " is invalid.");
  32. }
  33. }
  34. }
  35. public static void Validate(object validateObject)
  36. {
  37. System.Type t = validateObject.GetType();
  38. PropertyInfo[] ps = t.GetProperties();
  39. foreach(PropertyInfo pi in ps)
  40. {
  41. ValidateProperty(validateObject, pi.Name);
  42. }
  43. }

现在验证,只需要这样:

  1. try
  2. {
  3. Validator.Validate(form);
  4. }
  5. catch(Exception ex)

C# System.Attribute(验证类)的更多相关文章

  1. C# 通用验证类 支持 WPF,MVC,Winform

    验证方式,   通过继承 IDataErrorInfo接口 和 DataAnnotations 解释标记语言而实现, 为了能在WPF上通用,所了也要继承属性更改通知接口INotifyPropertyC ...

  2. C# - DataValid数据验证类

    从EasyCode 摘取下来的数据验证类 using System; using System.Collections.Generic; using System.Text; namespace Le ...

  3. 做一个牛XX的身份证号验证类(支持15位和18位)

    原文:做一个牛XX的身份证号验证类(支持15位和18位) #region 是否合法的中国身份证号码 protected bool IsChineseID() { if (str.Length == 1 ...

  4. JavaScript 数据验证类

    JavaScript 数据验证类 /* JavaScript:验证类 author:杨波 date:20160323 1.用户名验证 2.密码验证 3.重复密码验证 4.邮箱验证 5.手机号验证 6. ...

  5. System.IO.Directory类

    1.参考的博客:System.IO.Directory类和System.DirectoryInfo类(http://blog.sina.com.cn/s/blog_614f473101017du4.h ...

  6. 使用System.Timers.Timer类实现程序定时执行

    使用System.Timers.Timer类实现程序定时执行 在C#里关于定时器类有3个:System.Windows.Forms.Timer类.System.Threading.Timer类和Sys ...

  7. php表单数据验证类

    非常好用方便的表单数据验证类 <?php //验证类 class Fun{ function isEmpty($val) { if (!is_string($val)) return false ...

  8. 详解C#中System.IO.File类和System.IO.FileInfo类的用法

    System.IO.File类和System.IO.FileInfo类主要提供有关文件的各种操作,在使用时需要引用System.IO命名空间.下面通过程序实例来介绍其主要属性和方法. (1) 文件打开 ...

  9. JS表单验证类HTML代码实例

    以前用的比较多的一个JS表单验证类,对于个人来说已经够用了,有兴趣的可以在此基础上扩展成ajax版本.本表单验证类囊括了密码验证.英文4~10个 字符验证. 中文非空验证.大于10小于100的数字.浮 ...

随机推荐

  1. 创建线程的两种方式比较Thread VS Runnable

    1.首先来说说创建线程的两种方式 一种方式是继承Thread类,并重写run()方法 public class MyThread extends Thread{ @Override public vo ...

  2. dialog的传值

    A页面 等待B页面返回值的textBox:<asp:TextBox ID="TextBox1" runat="server"></asp:Te ...

  3. 此博客不更新文章,请到www.xiaoxiangyucuo.com看最新文章

    请到www.xiaoxiangyucuo.com看更多资料,包括Linux,JavaScript,Python,MySQL,PHP,HTML,Photoshop,以及各类软件下载. 希望大家支持,提出 ...

  4. struct timespec 和 struct timeval

    time()提供了秒级的精确度 . 1.头文件 <time.h> 2.函数原型 time_t time(time_t * timer) 函数返回从TC1970-1-1 0:0:0开始到现在 ...

  5. PHP学习心得(七)——常量

    常量的范围是全局的. 可以用 define() 函数来定义常量. <?php define("CONSTANT", "Hello world."); ec ...

  6. cms开发笔记2

    1 创建数据库表 //配置文件CREATE TABLE IF NOT EXISTS `mc_config` ( `en_name` varchar(80) NOT NULL, `ch_name` va ...

  7. RSA使用 常识

    1公钥加密,私钥解密  OK反过来, 私钥加密,公钥解密 也OK 2 使用RSA加密 对称算法的key ,用对称算法加密 消息.伙伴收到消息后,RSA解密出 对称算法的key,再用这个key去解密消息 ...

  8. jquery 里面的$(document).ready 和 DOMContentLoaded

    DOMContentLoaded 事件是当页面的初始dom tree加载好了就会执行. 而不会去等待外部的css 渲染完成和 js执行完成,即下图中的DOM Tree阶段 DOMContentLoad ...

  9. 转最简便安装python+selenium-webdriver环境方法

    最简便安装python+selenium-webdriver环境方法 from:http://www.easonhan.info/python/2013/12/07/active-python-ins ...

  10. python上下文管理器及with语句

    with语句支持在一个叫上下文管理器的对象的控制下执行一系列语句,语法大概如下: with context as var: statements 其中的context必须是个上下文管理器,它实现了两个 ...