关于“Attribute”
C#中“Attribute”一般译为“特性”,我有时也读成“标注”。
下面先看一个微软自定义的特性:
[Serializable] public class UserInfo { ............ }
像这样子,在一个类或者方法前面,贴上一个类似[Serializable]的标签,然后它就会起一些特别的作用了,Serializable就表示这个类是可序列化的,使用起来也就这么简单。
那么,它是怎么工作的呢?
我听过有人打过一个这样的比喻:就像是拆迁的在一栋房子上画了一个“拆”,然后就表示这个房子可以拆了,但我们应该知道,后面这栋房子被拆了,并不是这个“拆”字本身有什么神奇的力量,而是有人来把它给拆了!
对于特性来说,道理是一样的,当我们在一个方法或者是类或者其它地方写上这么一个特性以后,框架是会去解析它的,实质上,是通过C#的反射机制实现的。下面我们来看一个怎定义的特性就会明白了。
先还是介绍一下自定义特性的一些相关知识:
(1)、元特性AttributeUsage
AttributeUsage是微软自定义的一个特性,它只能写在一个自定义的特性前面,而不能当成是一个普通的特性来用,所以有些书上就称它为元特性。下面看我们的例子:
using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; namespace MyRequireAttribute { [AttributeUsage(AttributeTargets.All,AllowMultiple=false,Inherited=true)] public class RequireAttribute:Attribute { private string cnname; public string Cnname { get { return cnname; } set { cnname = value; } } } }
AttributeUsage后面有三个参数:
第一个AttributeTargets是一个枚举,它代表我们自定义的这个"RequireAtttibute"特性可以用于哪些地方,它的可选值有以下一些类型
public enum AttributeTargets { All=16383, Assembly=1, Module=2, Class=4, Struct=8, Enum=16, Constructor=32, Method=64, Property=128, Field=256, Event=512, Interface=1024, Parameter=2048, Delegate=4096, ReturnValue=8192 }
它们代表的意思一看便知,这里也不再解释了,只啰嗦一句如果使用多个的组合,应该这样:AttributeTargets.Method|AttributeTargets.Field,用“|”分开。
第二个参数AllowMultiple代表是否可以在一个地方写多个这样的特性。
第三个参数Inherited代表这个特性是否在它的继承类中也有效。
(2)、自定义的特性都应该继承自System.Attribute
(3)、特性的参数
自定义的特性中,如果有构造方法,那么,构造方法中的参数是必写的,特性中的其它属性则代表的是可选参数像上面自定义的特性,我就没有写构造方法,只有一个代表“中文名”的属性。
然后简单的介绍一下这个例子要做什么事
我想自定义一个RequireAttribute,用来标识一个字段不能为空。
(1)、自定义RequireAttribute(说明:这里写的是RequireAttribute,使用的时候可以简写成Require),代码就是上面写的那个了。
(2)、使用这个特性,下面我们定义一个类,然后为其中的每一个字段贴上一个标签,表示它们都不能为空,代码如下:
using System; using System.Collections.Generic; using System.Text; namespace MyRequireAttribute { public class UserInfo { private string name; [RequireAttribute(Cnname = "姓名")] public string Name { get { return name; } set { name = value; } } private string gender; [RequireAttribute(Cnname = "性别")] public string Gender { get { return gender; } set { gender = value; } } private string age; [RequireAttribute(Cnname = "年龄")] public string Age { get { return age; } set { age = value; } } private string phonenumber; [RequireAttribute(Cnname = "电话号码")] public string Phonenumber { get { return phonenumber; } set { phonenumber = value; } } } }
(3)、万事俱备,只欠东风。我们一开始就说了,自定义的特性就像拆迁的人在一栋房子上画了一个“拆”字,理它,它就起作用,不理它,它就不起作用。
我们建立一个winform程序,并把上面的两大块代码(userinfo和RequireAttribute)加进去。
winform的界面如下:
我想在点击这个“检测”的时候,看下上面的四个空是不是有空值。
后台代码是这样的:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; namespace MyRequireAttribute { public partial class Form1 : Form { public Form1() { InitializeComponent(); } //为了在下面的两个方法中都用这个对象,我们把它定义成字段。 UserInfo userinfo = null; private void btn_check_Click(object sender, EventArgs e) { userinfo = new UserInfo(); userinfo.Name = txb_name.Text; userinfo.Age = txb_age.Text; userinfo.Gender = txb_gender.Text; userinfo.Phonenumber = txb_phoneNumber.Text; this.CheckValue(); } private void CheckValue() { //用反射获取userinfo的类型 Type type = userinfo.GetType(); //用反射获取userinfo的成员信息 PropertyInfo[] propertyinfos = type.GetProperties(); //遍历所有成员 foreach (PropertyInfo propertyinfo in propertyinfos) { //获取所有特性 Attribute attr = Attribute.GetCustomAttribute(propertyinfo, typeof(RequireAttribute)); //验证当前成员是不是用了这个特性 if (attr != null && attr is RequireAttribute) { object propertyValue = propertyinfo.GetValue(userinfo,null); RequireAttribute ra= attr as RequireAttribute; string cnname = ra.Cnname; if (object.Equals("",propertyValue)) { MessageBox.Show(String.Format("{0}不能为空!",cnname)); } } } } } }
我们看到了,点击按钮后,我把四个textbox中值赋给了userinfo的实例。然后调用CheckValue方法来进行检测。
这里的CheckValue方法是非常关键的地方。它就是解析我们自定义的RequireAttribue的,里面用到了反射的一些知识,不一步步讲解,具体看代码中的注释。
至此,已经全部完成。我们点击“检测”的时候,就可以知道是不是有些值为空了。
例子的代码可以点击下载:自定义特性例子