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的方式】

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM