本文以一个项目中通用的验证类来举例说明如何使用自定义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. 九度OJ 1511 从尾到头打印链表

    题目地址:http://ac.jobdu.com/problem.php?pid=1511 题目描述: 输入一个链表,从尾到头打印链表每个节点的值. 输入: 每个输入文件仅包含一组测试样例. 每一组测 ...

  2. 深度模拟java动态代理实现机制系类之二

    这次我们要实现的是对任意接口,任意的方法进行特定的代理 这里不一样的只有Proxy类,要实现对所有方法进行代理,那么重点就在于获得接口的所有方法 import java.io.File; import ...

  3. VS2005调试时无法找到调试信息解决方法

    调试C++程序的时候出现,无法找到.exe的调试信息,或者调试信息不匹配.未使用调试信息生成二进制文件.解决方法:打开菜单项目->项目属性页: 1.选择配置属性->链接器->调试-& ...

  4. javascript中的sort()方法

    现在在学习javascript中,发现sort()函数是有点奇怪的东西(可能是本人水平的问题-_-!),于是就在这里记录一下自己找到的东西吧.sort()这个方法的参数很奇怪,必须是函数,但也是可选参 ...

  5. unix 常用命令

    (一)基本命令 命令格式: 命令 参数 1.ls 显示文件名,等同于dos下dir命令 命令格式:ls [option] file option: -l 显示详细列表 域1 :文件类型和文件权限 域2 ...

  6. Java Web开发之Servlet、JSP基础

    有好多年不搞Java Web开发了,这几天正好国庆放假,放松之余也有兴趣回头看看Java Web开发技术的基础. 我们都知道,Servlet是Java Web开发的重要基础,但是由于Servlet开发 ...

  7. MEF学习笔记

    之前公司里用到了一个叫MEF的东西,说来惭愧一直只管写代码却不曾理解MEF框架为何物,今天就来学习一下,这是一篇迟到了不知多久的博客. -------------------------------- ...

  8. this在JavaScript中的工作范围

    this在JavaScript中的工作范围 在一个函数中,this的行为,取决于JavaScript函数的调用方式和定义方式,而不仅仅是看它如何被定义的. var fullname = 'Fu';va ...

  9. 拓展:switch实现

    拓展:switch实现 参考中谷教育 python视频:switch  switch语句用于编写多分支结构的程序,类似于if ... elif ...else语句.switch语句表达的分支结构比if ...

  10. oracle忘记用户密码

    在cmd命令行下输入sqlplus / as sysdba alter user system identified by abc; 就可以将system用户的密码改成abc了. alter user ...