本文以一个项目中通用的验证类来举例说明如何使用自定义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. OpenJudge/Poj 1207 The 3n + 1 problem

    1.链接地址: http://bailian.openjudge.cn/practice/1207/ http://poj.org/problem?id=1207 2.题目: 总时间限制: 1000m ...

  2. vsftpd.conf配置详解

    根据 /etc/vsftpd/vsftpd.conf默认配置给出设定功能   # Example config file /etc/vsftpd/vsftpd.conf # The default c ...

  3. PHOTOSHOP 半透明方格

    1.新建60*60的透明文档,在左方和上方用直线工具画白边,存储为图案(编辑/定义图案) 2.新建图层,用油漆桶填充图案 3. 选择若干小方格,填充白色后设置不透明度50%

  4. MongoDB入门三步曲3--部署技术:主备、副本集和数据分片

    mongodb部署--主备.副本及数据分片 主备复制 副本集 数据分片 主备复制 主备复制是最基本的一种多点部署方案,在读写分离.热备份.数据恢复等方面具有重要作用. 在真实的生产环境,主备库肯定需要 ...

  5. 为什么Laravel是最成功的PHP框架?

    Laravel 是一个有着美好前景的年轻框架,它的社区充满着活力,相关的文档和教程完整而清晰,并为快速.安全地开发现代应用程序提供了必要的功能.在近几年对PHP 框架流行度的统计中,Laravel始终 ...

  6. Centos系统mysql 忘记root用户的密码

    Centos系统mysql 忘记root用户的密码: 第一步:(停掉正在运行的mysql) [root@maomao ~]# /etc/init.d/mysqld stop Stopping MySQ ...

  7. 将TIBCO Host 实例注册为Windows服务

    安装了TIBCO ActiveMatrix BPM及成功创建了ActiveMatrix Administrator 和 BPM Server后,每次都要手动启动tibcohost,比较麻烦,实际上TI ...

  8. Win8.1设置窗口背景颜色为护眼色

    更改注册表 window+R --->输入regedit(点击确定后进入注册表编辑器) 需要修改以下两个地方,重启电脑生效: [HKEY_CURRENT_USER\Control Panel\C ...

  9. Python创建list

    Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: >>> ['Mic ...

  10. DataGridView自动行号

    最近又用了一下DataGridView控件,需要显示行号,我们知道在.net中DataGridView控件默认是不显示行号(数据的记录行数)的,后来通过查资料发现可以在DataGridView控件的R ...