本文以一个项目中通用的验证类来举例说明如何使用自定义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. SVN查看提交日志的命令

    Windows下,使用svn客户端查看日志很方便,但是如果在linux下,常规查看日志的命令对于日志记录数比较多的时候,屏幕显示不了,就比较麻烦,后来发现了一个很有用的参数可以限制要查看的记录条数 查 ...

  2. Apache(一)

    以下介绍是基于centos,Apache/2.2.15,进行解释 (资料来自www.netcraft.com) Apache 模块化的软件 Apache=core+module(内核+模块) core ...

  3. html5新增标签兼容性

    很多低版本的浏览器是不识html5新增的标签的,所以为了解决浏览器兼容性问题,主要有两种方法: js可以创建我们自定义的标签,例如,我们可以用js语句 document.createElement(' ...

  4. JSON对象的stringify()和parse()方法

    1.stringify() ---- JavaScript对象序列化为JSON字符串 eg1. var book = {title: 'JS', authors: ['Van'], edition:3 ...

  5. springdata整合mongodb一些方法包括or,and,regex等等《有待更新》

    这几天接触mongodb以及springdata,自己英语比较戳,所以整理这些方法花的时间多了点,不过也是我第一次在外国网站整理技术 不多说,直接上代码,这里只是给出一些操作方法而已,如果有需要源码的 ...

  6. Yii Query Builder insert()、update()、delete()使用

    Yii自带的query builder还是很好用的,省去了拼sql的过程,今天在写一个语句的时候遇到这样一个问题 $connection = Yii::app()->db; $command = ...

  7. 使用ECLIPSE+MINGW搭建C/C++开发环境

    有个朋友要我帮忙跑一个C程序而我现在主要用java,电脑上也就没有C语言的编译和开发环境,在学习java的这段期间,接触到了Eclipse这个强大的IDE,用惯了.就为调试一个程序,去安装一个VS觉得 ...

  8. Oracle RAC备份异机单实例恢复演练

    本文只节选了操作方案的部分章节: 3.   操作步骤 3.1. 异机单实例Oracle数据库软件安装 在异机上进行单实例Oracle数据库软件安装.该步骤过程不再本文中重复描述,如果对安装过程存在疑问 ...

  9. 【转】oracle 针对中文字段进行排序

    1)按笔画排序 select * from Table order by nlssort(columnName,'NLS_SORT=SCHINESE_STROKE_M') 2)按部首排序 select ...

  10. Qt 5 常见错误汇总

    1.没加 QT+=sql,需要再.pro文件中加上 2.无故崩溃,这个有多种原因,多为指针问题,例如,指针内存泄露,指针未开辟空间直接使用,UI还未建立就使用UI里面的东西..等等 3.Sql问题,有 ...