zoukankan      html  css  js  c++  java
  • C# 特性【Attribute】【什么是特性?以及特性的一些修饰】

    特性【Attribute】是什么?

    概念:1. 特性AttriBute:就是一个类,能直接继承/间接继承自AttriBute父类;

               2. 约定俗成用Attribute结尾,标记时就可以省略,eg:[CustomAttribute] ---> [Custom];
               3. 可以用中括号包裹,标记到元素,其实就是调用构造函数【如果父类用了带参数的构造函数,特性调用只能改为以下结构---->[Custom(0)]】;
               4. 然后可以指定属性,字段,修饰参数,返回值,但是方法不可以;

    特性无处不在,比如我们经常用在元素上面添加的,[ ] 中括号形式的东西,基本上,我们在工作中遇到的各种的框架里面都有eg :EF-MVC-WCF-Webservice-UniTest-IOC-AOP-SuperSocket,

    如果,添加了[Serializable],就表示这个元素可以序列化,那特性究竟是什么呢?我们不妨按F12点进去看看,     

     

    可以看到这个特性SerializableAttribute就是一个类,继承于Attribute抽象类,那我们自己动手写一个试试

        public class CustomAttribute : Attribute
        {
        }
        [CustomAttribute]
        public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public void Study()
            {
                Console.WriteLine($"{this.Name}");
            }
            public string Answer(string name)
            {
                return $"This is {name}";
            }
        }

    我们同时又可以修改为以下格式:说明了什么? 说明:用Attribute结尾,标记时就可以省略,eg:[CustomAttribute]  ---> [Custom]

     

     那我们再进一步修改试试,继承CustomAttribute类,调用看看:

        public class CustomAttribute : Attribute
        {
        }
        public class CustomAttriButeChild : CustomAttribute
        {
        }

    那我们是不是就可以得到一个:【特性AttriBute:就是一个类,直接继承/间接继承自AttriBute父类】的结论

    那特性既然是一个类,那它里面又可以放什么东西呢?

     1.无参构造函数;

        public class CustomAttribute : Attribute
        {
            public CustomAttribute()
            {
                Console.WriteLine("这是一个无参数构造函数");
            }
        }
        public class CustomAttriButeChild : CustomAttribute
        {
        }

    2.int 类型的参数

        public class CustomAttribute : Attribute
        {
            public CustomAttribute(int Id)
            {
                Console.WriteLine("如果只有当前的这个构造函数,继承当前父类的子类会报错,why?");
            }
        }
        public class CustomAttriButeChild : CustomAttribute
        {
            public CustomAttriButeChild() : base(123)
            {
                Console.WriteLine("继承父类的子类报错,因为它继承了父类,但是它只有一个带参数的构造函数,那么调用也必须显示指定调用");
            }
        }

    3.无参,int,string 同时存在的情况呢?

        public class CustomAttribute : Attribute
        {
            public CustomAttribute()
            {
                Console.WriteLine("这是一个无参数构造函数");
            }
            public CustomAttribute(int Id)
            {
                Console.WriteLine("如果只有当前的这个构造函数,继承当前父类的子类会报错,why?");
            }
            public CustomAttribute(string name)
            {
                Console.WriteLine("string类型的构造函数");
            }
        }
        public class CustomAttriButeChild : CustomAttribute
        {
            public CustomAttriButeChild() : base(123)
            {
                Console.WriteLine("继承父类的子类报错,因为它继承了父类,但是它只有一个带参数的构造函数,那么调用也必须显示的指定调用");
            }
        }

     

    这是分开调用特性的情况,那我们一起调用呢?

        

        [Custom(0)]--- [Custom]---[Custom()]上面三个分开都可以调用,但是如果同时调用就会提示特性重复,默认情况不允许,那么我怎么可以做到同时使用多个特性呢?我们加"[AttributeUsage]"特性试试

       

      加上这个[AttributeUsage]特性之后编译器,就没有在显示特性重复,是不是说明这个特性影响编译器,

      我们进去看看它里面都有些什么元素,

        

      AttributeTargets.All,表示可以修饰任何目标元素 ,那我们更换一个呢?

      

        为什么会报错??因为AttributeTargets.Method----->只能用来修饰方法

        

        那我们希望它又可以修饰方法,又可以修饰属性,又可以修饰类呢?

        

        [AttributeUsage]特性,影响编译器,它能-----指定修饰的对象------能否重复修饰---修饰的特性子类是否继承 ---> [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =true)]

       特性还可以指定属性,字段

        public class CustomAttribute : Attribute
        {
            public CustomAttribute()
            {
            }
            public CustomAttribute(string name)
            {
            }
            public CustomAttribute(int Id)
            {
               
            }
    
            public string Remake;
            public string Description { get; set; }
            public void Show()
            {
    
            }
        } 

       

      同时字段还能修饰参数,返回值

            /// <summary>
            /// 特性在方法的参数前面,用来修饰参数的
            /// [return:Custom]还可以修饰返回值
            /// </summary>
            [return: Custom]
            public string Answer([Custom]string name)
            {
                return $"This is {name}";
            }

    特性多重修饰写法:

    1     //方法一:
    2     [Custom()]
    3     [CustomAttriButeChild]
    4     [Custom(0) ]
    5     [Custom(0, Remake= "字段")]//构造函数的传递方式:是直接传值,字段需要带Remake
    6     [Custom(0,Remake ="1115",Description = "属性")]
    1      //方式二:
    2      [return: Custom, Custom, Custom(0), Custom(0, Remake = "1115", Description = "属性")]

     那问题来了,看了这么多,特性到底有什么用???让我们接着往下面探讨

           //程序入口
            static void Main(string[] args)
            {
                Student student = new Student() 
                { 
                    Id = 1, Name = "Attribute" 
                };
                student.Study();
                student.Answer("");
            }

    跟踪发现写了那么多特性根本就没什么用,我们自定义的特性,看起来好像毫无意义的样子,那框架提供的特性究竟是怎么产生价值的呢??

    那我们新建一个studentVip类反编译看看:

        public class StudentVip : Student
        {
            public string VipGroup { get; set; }
            public void DoHomeWork() 
            {
            }
        }

       编译结果展示:

                             

     我们加上我们自定义的特性反编译试试:

        [Custom("123",Remake ="VIP",Description ="Hello!")]
        public class StudentVip : Student
        {
            [Custom("123", Remake = "VIP", Description = "Hello!")]
            public string VipGroup { get; set; }
            [Custom("123", Remake = "VIP", Description = "Hello!")]
            public void DoHomeWork() 
            {
            }
        } 

    反编译之后得到的结果:

                

      反编译之后,发现特性会在元素的内部生成.custom的东西,那我们看一下框架里面的特性,加上编译以后又有什么变化呢?

      

       框架特性也是一样,我们C#访问不到,是不是可以理解为特性没有产生任何变化,但框架究竟是怎么产生功能的呢?也就是怎么在程序运行的时候,能够找到特性的呢?---反射

       我们如何在程序运行中用反射去找到特性?可以从类型 属性 方法 都可以获取特性实例,先IsDefined判断检测,通过反射在构造实例,再获取(实例化)

       我们新建一个InvokeCenter类来看看:

        public class InvokeCenter
        {
            /// <summary>
            /// 一定要先IsDefined判断检测,通过反射在构造实例,再获取
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="student"></param>
            public static void MangerStudent<T>(T student) where T : Student
            {
                //打印属性
                Console.WriteLine($"{student.Id}_{student.Name}");
                student.Study();
                student.Answer("123");
    
                Type type = student.GetType();
                //检查特性是否存在
                if (type.IsDefined(typeof(CustomAttribute), true))
                {
                    //获取列表找出全部,也可以只找一个type.GetCustomAttribute--这种方式使用场景比较多
                    Object[] oAttributeArray = type.GetCustomAttributes(typeof(CustomAttribute), true);
                    foreach (CustomAttribute attribute in oAttributeArray)
                    {
                        attribute.Show();
                    }
                    
                    //循环所有的属性
                    foreach (var prop in type.GetProperties())
                    {
                        //如果这个属性包含这个特性
                        //那么我们就获取到包含这个特性属性的列表,它是这个数组集合
                        if (type.IsDefined(typeof(CustomAttribute), true))
                        {
                            Object[] OAttributeProp = type.GetCustomAttributes(typeof(CustomAttribute), true);
                            foreach (CustomAttribute attribute in OAttributeProp)
                            {
                                attribute.Show();
                            }
                        }
                    }
                    //把所有的方法找出来
                    foreach (var method in type.GetMethods())
                    {
                        //判断是否具有特性
                        if (type.IsDefined(typeof(CustomAttribute), true))
                        {
                            Object[] oAttributeMethod = type.GetCustomAttributes(typeof(CustomAttribute), true);
                            foreach (CustomAttribute attribute in oAttributeMethod)
                            {
                                attribute.Show();
                            }
                        }
                    }
                }
            }
        }
        //前面我们自定义的CustomAttribute特性的部分代码修改
        [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =true)]
        public class CustomAttribute : Attribute
        {
            public CustomAttribute()
            {
                Console.WriteLine($"{this.GetType().Name} 无参数构造函数执行");
            }
            public CustomAttribute(string name)
            {
                Console.WriteLine($"{this.GetType().Name} string参数构造函数执行");
                this._Name = name;
            }
            public CustomAttribute(int Id)
            {
                Console.WriteLine($"{this.GetType().Name} int参数构造函数执行");
                this._Id = Id;
            }
            private int _Id = 0;
            private string _Name = "";
    
            public string Remake;
            public string Description { get; set; }
            public void Show()
            {
                Console.WriteLine($"{this._Id}_{this._Name}_{this.Remake}_{this.Description}");
            }
    
        }
             //程序入口调用跟踪
            static void Main(string[] args)
            {
                {
                    Student student = new StudentVip()
                    {
                        Id = 2, Name = "特性"
                    };
                    InvokeCenter.MangerStudent<Student>(student);
                }
              }

    跟踪的结果展示:以及为什么会有对应条数截图的说明:

      

     结论:程序运行时可以找到特性,那就可以发挥特性的作用,提供额外的信息,行为,特性本身是没有用的,需要一个第三方InvokeCenter,在这里主动检测并提供特性,才能提供功能,

               那么框架的特性方式也是一样的,框架里面已经集成完,自己去检测特性,另外,特性是在编译前就已经确定好了,构造函数/属性/字段,都不能用变量

              【所以MVC5-filter 是不能注入的,只有在core里面才提供了注入filter的方式】

  • 相关阅读:
    zsh: command not found: mysql
    ReactiveX -Rx
    mac 安装mysql
    mac 完全卸载mysql
    gradle 编译OutOfMemoryError
    Nginx均衡负载(IP_HASH)未生效
    Tomcat集群+Nginx+Redis服务搭建
    Spring之SpringMVC的RequestToViewNameTranslator(源码)分析
    Spring之SpringMVC的MethodNameResolver(源码)分析
    Spring之SpringMVC前端控制器DispatcherServlet(源码)分析
  • 原文地址:https://www.cnblogs.com/wangwangwangMax/p/13977052.html
Copyright © 2011-2022 走看看