C#基础之Attribute

1.什么是Attribute

特性简单点理解就是为目标元素添加一些附加信息,这些附加信息我们可以在运行期间以反射方式拿到。目标元素指的的是程序集、模块、类、参数、属性等元素,附加信息指的是特性类中的成员。可以看出特性类其实就是一个数据结构,我们可以将各种各样的信息放入这个类中,并将特性类指定目标元素中,在目标元素中每关联一个特性就创建一个特性类实例,当然它的作用还不知如此。

下面是使用特性的3段代码,分别是3个类

第一个是MyAttribute特性类,第二个是与特性类关联的目标类MyClass,第3个类是主程序类Program。

在第一个类中,我使用了AttributeUsage系统特性类,.net还为我们提供了很多固定特性类,比如Serializable、Conditional、Dlllmport、Flags等

AttributeUsage类的参数作用在代码中已经有了注释,可以添加这个特性也可以不添加,但这个特性类在我这个程序中必须添加。

yuan原因是我在第二个类MyClass中添加了4个MyAttribute特性实例,而默认情况下只允许t添加一个实例。因此我得在AttributeUsage中指定AllowMultiple为true,如果不指定编译会报错。

 在AttributeUsage特性类的参数中,并不是完全以传值的形式创建的实例,其中还可以直接给参数赋值比如AllowMultiple和Inherited。

对于前者一般是构造函数中的参数,我们必须给构造函数赋值否则就不能初始化了,因此这种类型的参数是固定参数。对于后者则是可选参数,细心点会发现它就是特性类中的属性,比如在第二个类MyClass中就给Hobby属性赋值了。正因为参数里有可选参数,故MyClass可以同时关联4个特性实例。在第三个类中,我通过GetCustomAttribute拿到定制的特性数组,其实就是MyAttribute的实例,这样就可以通过实例对象获取里面的数据成员了。这便是Attribute的基本使用,由于使用时需要调用构造函数,因此定制特性类必须有公共构造函数。将程序的exe文件放入Reflector中可以很清楚的看到特性就是一个类,使用特性其实就是调用特性的构造函数。

//特性也可以用在特性上
    [AttributeUsage(
        AttributeTargets.All,     //目标元素可以是任何元素
        AllowMultiple=true,     //多个特性可以加在一个元素上,为false时一个元素上只允许有这个特性的唯一实例
        Inherited=true)]           //特性可被子类继承,为false时不可以被继承
    class MyAttribute : Attribute
    {
        //字段
        public string name="默认名字";
        public int age = 0;
        string hobby="默认爱好";

        //属性
        public string Hobby
        {
            get { return hobby; }
            set { hobby = value; }
        }
        //构造方法
        public MyAttribute()
        {

        }
        public MyAttribute(string name, int age)
        {
            this.name = name;
            this.age = age;
        }

        //实例方法
        public void haha()
        {
            Console.WriteLine("哈哈");
        }
    }
[My()]
    [My(Hobby="足球")]
    [My("小方",20)]
    [My("小白",30,Hobby="篮球")]
    class MyClass
    {
        public void haha()
        {
            Console.WriteLine("我是MyClass类");
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            /*
             * 本来想看一下会不会默认拿第一个实例,结果执行时报错:找到同一个类型的多个实例
            //拿到MyClass上的一个特性实例
            MyAttribute myAttribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(MyClass), typeof(MyAttribute));
            //看看这个实例是哪一个
            if (myAttribute!=null)
            {
                Console.WriteLine(myAttribute.name);
                Console.WriteLine(myAttribute.age);
                Console.WriteLine(myAttribute.Hobby);
                myAttribute.haha();
            }
            */

            //拿到MyClass上的特性实例数组,这里有4个MyAttribute的实例
            MyAttribute[] myAttributes = (MyAttribute[])Attribute.GetCustomAttributes(typeof(MyClass), typeof(MyAttribute));
            MyAttribute myAttribute = myAttributes[0];
            if (myAttribute != null)
            {
                Console.WriteLine(myAttribute.name);
                Console.WriteLine(myAttribute.age);
                Console.WriteLine(myAttribute.Hobby);
                myAttribute.haha();
            }
            Console.ReadLine();
        }
    }
    /*执行结果:
                    小白
                    篮球
                    哈哈
     */

 2.Attribute的作用

因为特性的存在,让我们可以在程序运行时得到一些信息,再根据这些信息进行逻辑判断。比如可以使用特性来确保Model对象的数据全部去除空字符串,代码如下面所示。第一段代码指特性类MyAttribute,第二段代码指使用特性的MyClass类,第三段代码指MyClass类的扩展方法Trim,第四段代码指主程序类Program。在main方法中创建了一个myclass对象,在给这个对象赋值时特意指定了一些空格。假设现在需要将MyClass这个类作为数据库实体类People,它的实例存放着输入的数据,这个数据可能有空格。一般情况下我得调用trim()方法来去除空格,但是如果MyClass的属性很多的话那就很麻烦了,需要写很多ToString().Trim()方法。而使用特性+扩展方法则可以轻松很多,对于需要进行空格去除的属性添加一个MyAttribute特性,接着调用实例对象的Trim扩展方法。在Trim方法中,会遍历这个对象的所有属性,接着遍历每个属性的所有特性,并找到打了MyAttribute特性的属性,接着进行ToString().Trim()方法的调用并重新给属性赋值,这样只需写一句myclass.Trim()就可以实现除掉空格的功能。如果没有特性,虽然一样可以使用扩展方法来对属性进行去除空格,但是我们无法对指定的属性进行去除,只能一口气把所有类型为string的字符串空格全都去除。

 

[AttributeUsage(AttributeTargets.Property,Inherited=false,AllowMultiple=false)]
    public  class TrimAttribute : Attribute
    {
        //字段与属性
        readonly Type myType;
        public Type MyType
        {
            get { return this.myType; }
        }
        //构造函数
        public TrimAttribute(Type type)
        {
            myType = type;
        }
    }
class MyClass
    {
        [TrimAttribute(typeof(string))]
        public string Name { get; set; }

        [TrimAttribute(typeof(string))]
        public string Hobby { get; set; }

        [TrimAttribute(typeof(string))]
        public string Address { get; set; }
    }
//扩展方法必须是静态类,静态方法。
    public static class TrimAttributeExtension
    {
        public static void Trim(this object obj)
        {
            Type t = obj.GetType();
            //得到myclass实例对象的所有属性
            foreach (PropertyInfo prop in t.GetProperties())
            {
                //得到某个属性上的所有特性
                foreach(var attr in prop.GetCustomAttributes(typeof(TrimAttribute),true))
                {
                    TrimAttribute trimAttribute = (TrimAttribute)attr;
                    //获得obj的prop属性的值
                    object o=prop.GetValue(obj, null);
                    //如果o不为null且这个属性上的特性实例的MyType属性是string类型
                    if (o!= null && (trimAttribute.MyType == typeof(string)))
                    {
                        //重新给这个属性赋值,也就是已经Trim()后的,可以看到GetPropertyValue(obj, prop.Name)其实就是o。
                        object newValue = GetPropertyValue(obj, prop.Name).ToString().Trim();
                        prop.SetValue(obj, newValue, null);
                    }
                }
            }
        }
        //拿到属性本身所表示的值
        private static object GetPropertyValue(object instance, string propertyName)
        {
            //首先得到instance的Type对象,然后调用InvokeMember方法,
            //这个方法的第一个参数意思是你需要调用的属性、方法、字段的”名字“,第二个参数是你调用propertyName是要干什么,
            //这里是拿到属性,第四个是要操作的实例。最后是需要传入的参数,这里调用属性因此不需要参数我就设置为null了。
            return instance.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, instance, null);
        }
}
class Program
    {
        static void Main(string[] args)
        {
            MyClass myclass = new MyClass();
            myclass.Name = "小方      ";
            myclass.Hobby = "  篮球     ";
            myclass.Address = "     湖北";
            myclass.Trim();
            
            /*
            执行到这里会看到上面三个属性的值中空格全部都没有了
            myclass.Name = "小方";
            myclass.Hobby = "篮球";
            myclass.Address = "湖北";
             */
            Console.ReadLine();
        }
    }

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值