本文以一个项目中通用的验证类来举例说明如何使用自定义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. ubuntu获取硬盘的uuid。

    1.用UUID来标识硬盘有很多好处,它是一个硬盘的唯一代号,所以当硬盘插口位置变化时,虽然sda可能会变成sdc,但这个码是不会变的.所以在 fstab中用/dev/sda1这样的硬盘标识可能会有混乱 ...

  2. phpstudy 下开启openssl

    1.在php.ini里面的extension=php_openssl.dll是开启的状态,没有被注释. 2.里面的allow_url_fopen = On表示开启了. 3.如果你是Windows 64 ...

  3. C#基础(六)——值类型与引用类型

    CLR支持两种类型:值类型和引用类型. 值类型包括C#的基本类型(用关键字int.char.float等来声明),结构(用struct关键字声明的类型),枚举(用enum关键字声明的类型):而引用类型 ...

  4. Oracle表空间传输测试

    源数据库平台:window 7 64bit Oracle 11g 64bit目标数据库平台:RHEL6 64bit Oracle 11g 64bit 1.查看数据集 select * from nls ...

  5. Stop a hung service 关闭一个无响应的windows 服务

    If you ever have trouble with a service being stuck in a 'starting' or 'stopping' state, you can run ...

  6. 上传图片+浏览+裁切 Demo(无后台处理部分)

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. VisualStudio自定义代码段_方法一

    在VisualStudio里,使用代码段会提高我们的编写速度.其实,就是给一段代码加个快捷方式,使用时,快捷方式按键+2次Tab键. 举个例子: 比如输入Console.WriteLine (); 传 ...

  8. android:Faild to install,你的主机中的软件终止了一个连接错误解决

    当在用真机调试android程序时出现Faild to install,你的主机中的软件终止了一个连接错误时可以这样解决: 在手机开启usb调试和安装未知来源软件的情况下: 1:先查进入任务管理器查看 ...

  9. Codeforces Round #209 (Div. 2)

    A: 要么是两次要么4次,判断是否在边界: #include<cstdio> using namespace std; int main() { int n,m,x; ; scanf(&q ...

  10. Spring+MyBatis实践—登录和权限控制

    1.实现用户登录功能: 通过session来实现用户登录功能.在用户登录时,将用户的相关信息放在HttpSession对象用,其中HttpSession对象可以通过HttpServletRequest ...