关于“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的,里面用到了反射的一些知识,不一步步讲解,具体看代码中的注释。
至此,已经全部完成。我们点击“检测”的时候,就可以知道是不是有些值为空了。
例子的代码可以点击下载:自定义特性例子

posted @ 2012-12-03 10:10  伯箫  阅读(368)  评论(0编辑  收藏  举报