net 反射30分鍾速成


 

  • 概述 什么是反射

Reflection,中文翻譯為反射。
        這是.Net中獲取運行時類型信息的方式,.Net的應用程序由幾個部分:‘程序集(Assembly)’、‘模塊(Module)’、‘類型(class)’組成,而反射提供一種編程的方式,讓程序員可以在程序運行期獲得這幾個組成部分的相關信息,例如:

        Assembly類可以獲得正在運行的裝配件信息,也可以動態的加載裝配件,以及在裝配件中查找類型信息,並創建該類型的實例。
Type類可以獲得對象的類型信息,此信息包含對象的所有要素:方法、構造器、屬性等等,通過Type類可以得到這些要素的信息,並且調用之。
MethodInfo包含方法的信息,通過這個類可以得到方法的名稱、參數、返回值等,並且可以調用之。
諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection命名空間下。

 

類型 作用
Assembly 通過此類可以加載操縱一個程序集,並獲取程序集內部信息
EventInfo 該類保存給定的事件信息
FieldInfo 該類保存給定的字段信息
MethodInfo 該類保存給定的方法信息
MemberInfo 該類是一個基類,它定義了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多個公用行為
Module 該類可以使你能訪問多個程序集中的給定模塊
ParameterInfo 該類保存給定的參數信息      
PropertyInfo 該類保存給定的屬性信息

 

 

 

這些都是廢話,我們一起看幾個案列就完全學會了,在此說明下,反射用到的一些基礎技術有 運行運算符,type 類,這里就不過多的解釋了,如有不會可以去園子里面自己去找,本人也寫過一篇相關文章,簡單的介紹了運行運算符。

 

  • 如何得到一個類的對象

現有工程文件(項目文件)結構如下

People類代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Entity
{
    public class People
    {

        public People()
        {
            Console.WriteLine("People被創建了");
        }
        public People(String Name)
        {
            this.Name = Name;
            Console.WriteLine("People被創建了,並且people的名字是"+this.Name);
        }
        public string Name { get; set; }//自動屬性,在程序實例化的過程中會自動創建私有的字段,這個字段在people 內存中開辟控件存儲其值(本文稱為公有屬性)在此感謝ENC博主的支持和評論,
        public int Age { get; set; }

        public string Sex { get; set; }

        public string msg;//公有字段

        private string qq;//私有字段
        private string address;//私有屬性
        public string Address { get => Address; set => Address = value; }
        public override string ToString()
        {
            return "{" + $"name:{this.Name},age:{this.Age},sex{this.Sex}" + "}";
        }
        public string Say()
        {
            return "hello! " + this.Name;
        }
    }
}

 

 debug 目錄如下:

 

 

 這里說明下,程序中,並沒有引用 Entity 類庫,也沒有引用Entity..DLL文件,請自行引用,我們如果不實例化得到一個對象呢??正常的時候,我們都是通過new 得到一個對象,如:

using Entity;
using System;
using System.Collections.Generic;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            People p = new People();
            Console.WriteLine(p);
            People peop = new People("張三");
            Console.WriteLine(p);
            Console.Read();
        }
    }
}

我們再來看下類的類型是什么?

 

using Entity;
using System;
using System.Collections.Generic;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            Type t = typeof(People);
            Console.WriteLine(t);
            Type type= Type.GetType("People");
            Console.WriteLine(type);//這里是得不到的,因為配件裝載只能在程序集內部使用
            Console.Read();
        }
    }
}

我們來學習下,如何根據類類型進行反射。

  • 類的反射

    對象無參構造函數反射

 

 static void Main(string[] args)
        {
            Type type = typeof(People);
            People people= Activator.CreateInstance(type) as People;//實例化得帶一個類
            Console.WriteLine(people);
            Console.Read();
        }

 

    對象有構造函數參反射

 static void Main(string[] args)
        {
            Type type = typeof(People);
            People people= Activator.CreateInstance(type) as People;//實例化得到一個類
            Console.WriteLine(people);
            //實例化得到一個類,該類有一個參數
            People p = Activator.CreateInstance(type, new object[] { "Wbcsky" }) as People;
            Console.WriteLine(p);
            Console.Read();
        }

 

    對象泛型反射

 static void Main(string[] args)
        {
            Type type = typeof(People);
            People p1 = Activator.CreateInstance<People>();
            Console.WriteLine(p1);
            Console.Read();
        }

關於對象的反射,就只有這三種形式,分別是泛型反射,泛型反射有且只能得到無參數的實例對象,和普通無參反射像比較,反射反射減少了裝箱拆箱的操作。有參數反射我們是按照參數的順序,傳遞的object 數組。這些反射都是基於 Activator.CreateInstance 來完成的。

 

屬性字段的反射
  • 獲取一個對象的所有屬性

 

   static void Main(string[] args)
        {
            Type type = typeof(People);
            System.Reflection.PropertyInfo[] p = type.GetProperties();
            foreach (var item in p)
            {
                Console.WriteLine("屬性名:" + item.Name + "屬性類型" + item.PropertyType.FullName + "屬性類型命名空間" + item.PropertyType.Namespace);
            }
            Console.Read();
        }

我們都知道,在C#中,屬性的封裝有兩種,一種全寫,一種簡寫,全寫的在某些工具書中叫做私有屬性,簡寫的在工具書上叫做公有屬性。

如: 

   public int Age { get; set; }
我們稱為簡寫,工具書上叫做公有屬性。
則:
      private string address;//私有屬性
        public string Address { get => Address; set => Address = value; }

private string iD;
public string ID

{
get { return this.iD; }
set { this.iD = value; }
}

這種寫法我們稱為私有屬性,私有屬性中,當使用=>這種運算的,我們稱為lambda表達式寫法,使用this 關鍵字的寫法,我們稱為面向對象寫法。不論哪一種屬性,我們都叫做屬性,我們在反射中獲取屬性使用的是Type 類的 .GetProperties()方法來獲取類的全部屬性。我們來看下執行結果。

這里就不過多的介紹獲取屬性的值了,我們在下面介紹獲取屬性的值。
  • 獲取指定名稱的屬性和值及設置一個值

  static void Main(string[] args)
        {
            Type type = typeof(People);
            System.Reflection.PropertyInfo Property = type.GetProperty("Name");//注意屬性名稱字符串大小寫
            if (Property == null) Console.Read();//如果屬性名稱大小寫錯誤或者不存在,我們Property對象將會是null
            Console.WriteLine("屬性名:" + Property.Name + "屬性類型" + Property.PropertyType.FullName + "屬性類型命名空間" + Property.PropertyType.Namespace);
            //獲取屬性的值
            People p= Activator.CreateInstance(type) as People;//獲取對象
            object oName = Property.GetValue(p); //獲取值
            Console.WriteLine("" + oName);
            Property.SetValue(p, "abc");//設置一個值
            oName = Property.GetValue(p); //獲取值
            Console.WriteLine("" + oName);
            Console.Read();
        }

看了上面的代碼,我們會發現,獲取屬性使用的是Type類的 GetProperty方法來完成的。獲取值和設置值,使用的是 PropertyInfo  類的 GetValue和Set value 來完成的。執行結果如下

因為初始化的時候是空,所以舊就什么也沒有輸出。有人會說了,這個沒有獲取到類,進行點寫的方便,為什么要這么寫呢,告訴你一句話,存在就是有道理的,這里可以簡單的告訴,我們很多時候,一個功能更新過於頻繁,我們完全可以把這個類寫入配置文件中,去配置這個類對象的功能使用。理解即可,不理解清背下來代碼。

  • 獲取對象的所以公有字段和私有字段

 在這里說明下,很多人都不明白字段和屬性的區別,我這里簡單說下,理解即可,不理解不影響學習,我們一個類的變量進行封裝,會出現get ,set 設置這個字段的訪問權限,這個封裝我們稱為屬性,而這個變量我們叫做字段,字段不指定修飾符的時候默認為私有的。

   static void Main(string[] args)
        {
            Type type = typeof(People);
            System.Reflection.FieldInfo[] fi = type.GetFields();
            Console.WriteLine("\r\n-------------------- 獲取對象的所以公有字段-------------------------------\r\n");
            foreach (System.Reflection.FieldInfo item in fi)
            {
                Console.WriteLine("公有字段名" + item.Name);
            }
            Console.WriteLine("\r\n-------------------- 獲取對象的所有私有字段-------------------------------\r\n");
            System.Reflection.FieldInfo[] fiprivate = type.GetFields(System.Reflection.BindingFlags.Instance |
                System.Reflection.BindingFlags.NonPublic);

            foreach (System.Reflection.FieldInfo item in fiprivate)
            {
                Console.WriteLine("私有字段名" + item.Name);
            }
            Console.Read();
        }

這是一個難點,但是在實際開發過程中很少使用,但是這我們必須要會,否則后期寫組件開發等文檔,該看不懂了,准備好瓜子,咱們開始聽故事了。

看了上面的代碼,及字段及屬性的介紹,我們會發現,輸出的結果,共有的很好理解,我們類里面定義的變量 指定public 以后,我們就可以通過

GetFields ()
方法返回我們想要的公有字段數組,我們輸出了名字,這里就不過多的解釋了。
反射私有字段,輸出的這個是什么啊,亂碼七招的。

私有字段名<Name>k__BackingField
私有字段名<Age>k__BackingField
私有字段名<Sex>k__BackingField
私有字段名qq
私有字段名address

 其實很好理解,我們在前面說過獲取所有屬性的時候說過屬性分為私有和公有,其中私有屬性有兩種寫法,其實私有屬性是對私有變量的封裝,也可以說是對私有字段的封裝,公有屬性是什么呢?
其實公有屬性在編譯過程中, 為了方便JTL 公共語言運行環境更好的編譯,自動生成了一個私有的字段,這個字段是根據操作系統不同生成不同前綴的私有字段,這里生成的是K_前綴的。這樣我們就好理解為什么上圖會多輸出三個字段。
如果此處還不理解,那么請看其他博客吧本文介紹的畢竟都是基礎。而實際開發過程中反射這基本使用的都是組件。


  • 獲取指定的公有字段

在這里就不介紹獲取指定公有字段的值了,和屬性獲取是一樣的。

 static void Main(string[] args)
        {
            Type type = typeof(People);
            Console.WriteLine("\r\n-------------------- 獲取對象的指定公有字段-------------------------------\r\n");
            Console.WriteLine("字段名" + type.GetField("msg").Name);
            Console.Read();
        }

 

代碼很簡單,只有一行。那么有人會問,那字段分為私有和共有的,為啥沒有介紹獲取私有屬性的呢???為啥沒有介紹獲取指定私有字段的呢???,其實答案很簡單,你看過有封裝屬性的時候有私有的嗎,私有的是不是都說在類的內部使用,那我反射類就可以了,我外部也不使用。那私有字段呢,為啥沒有,不是沒有,是有但是基本不使用,因為共有屬性會默認生成私有字段,這個私有字段的前綴不同,所以無法獲取,沒意義。所以基本沒人使用。

 

方法和構造函數的反射
  •  獲取公有方法並調用

  Type type = typeof(People);
            Console.WriteLine("\r\n-------------------- 獲取對象的共有方法並且調用-------------------------------\r\n");
            System.Reflection.MethodInfo mi = type.GetMethod("Say");
            People p= Activator.CreateInstance<People>();
            p.Name = "張四伙";//為了省事,這里不使用屬性反射添加值了
            object oReturn = mi.Invoke(p, null);//第一個參數為反射的對象,第二個參數object 數組,為參數,參數按順序填寫
            Console.WriteLine(oReturn);
            Console.Read();

 

 這個沒有什么解釋的了,前面最難的屬性字段反射,我們都會了,這個就不是問題了,自己多看看代碼?

  • 獲取當前類下的所有夠着函數

static void Main(string[] args)
{
Type type = typeof(People);
///獲取所有的一般不會使用,這里就不過多介紹了
System.Reflection.ConstructorInfo[] info = type.GetConstructors();//獲取當前類下所有夠着函數
foreach (System.Reflection.ConstructorInfo item in info)
{
Console.WriteLine("是否為虛方法"+item.IsVirtual);
Console.WriteLine("名稱"+item.Name);
}

Console.WriteLine("\r\n-------------------- 獲取當前類下參數類型匹配的夠着函數-------------------------------\r\n");
System.Reflection.ConstructorInfo con = type.GetConstructor(new Type[] { typeof(string) });
object o = con.Invoke(new object[] { "zhangsan" });
People peo = o as People;
Console.WriteLine(peo);
Console.Read();
}

 

大家會說了,夠着函數不就是類對象的實例化嗎?,我們前面不是講過反射類對象了嗎,為什么這個里面還要獲取實例化對象呢?

其實有些時候,我們在使用抽象類和接口的時候,我們通過之前學習的類的反射是一樣可以做到得到類的對象,這里之說以這么講解,因為有一些反射項目在優化的時候,會使用內部查找原則,即從夠着函數開始得帶類的對象,效率會更高一些。

我們在開發過程中,盡量有內而外,盡量把計算或者聲明拿到程序代碼執行過程中的最后去做,這樣使用內存會少,效率會更高。

下邊我們學習這篇文章的第二大核心。程序集反射

 

程序集反射

 

什么是程序集反射呢,加入我們三層架構,我不想引用bll層和model 層,也不想引用他們的dll,就能在業務層得帶他的對象引用,這個怎么做到呢???我們一起來學習下吧!

首先程序集中刪除Entity.dll 程序編譯跟目錄放置 ectity.dll文件。看下列代碼

 

using System;
using System.Collections.Generic;
using System.Data;

namespace testData
{
    class Program
    {
        static void Main(string[] args)
        {
            /*裝載程序集*/
            System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("Entity");
            // System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom("Entity.bll");//使用這種方式需要寫擴展名
            Console.WriteLine("\r\n-------------------- 程序集反射1-------------------------------\r\n");
            Type peopleType = assembly.GetType("Entity.People");//得到people 類的type 類型
            object obj = Activator.CreateInstance(peopleType);
            System.Reflection.MethodInfo me = peopleType.GetMethod("Say");
            object ret = me.Invoke(obj, null);
            Console.WriteLine(ret);
            Console.WriteLine("\r\n-------------------- 程序集反射2-------------------------------\r\n");
            object PeopleObj = assembly.CreateInstance("Entity.People");//直接得到類的實例化對象
            Console.WriteLine(PeopleObj);
            Console.Read();
        }
    }
}

 

 

代碼注釋已經很明確了,這里就不過多的解釋了,我們來看下執行結果 。

 

-------------------- 程序集反射1-------------------------------

People被創建了
hello!

-------------------- 程序集反射2-------------------------------

People被創建了
{name:,age:0,sex}

 

在程序集反射中,我們就沒有辦法在.屬性 .字段 .方法的調用了,這個時候,我們只能通過屬性,方法的反射去調用了,這里演示的不多,就兩種常用的案列,剩下的程序集有參數,無參數夠造函數就不多說了,和前面的是一樣的,本文只是介紹了開發過程中常用的案列。

 

泛型反射

 現有泛型類如下 

  public class GenericClass<T, W, X>
    {
        public void Show(T t, W w, X x)
        {
            Console.WriteLine("t.type={0},w.type={1},x.type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name);
        }
    }

 

反射代碼如下:

 Assembly assembly = Assembly.Load("Entity");
                    Type genericType = assembly.GetType("Entity.GenericClass`3");
                    Type typeNew = genericType.MakeGenericType(typeof(int), typeof(int), typeof(int));
                    Dynamic dGeneric = Activator.CreateInstance(typeNew);

 

泛型反射,我們有幾個泛型參數,我們就在后邊補位“`3”,注意符號 ` 可千萬別少了,我們泛型反射是使用Type 類的 MakeGenericType()方法進行獲取泛型的Type 類型的,通過這個類型進行反射

 
        

 

總結及擴展

 1.反射一般是用在序列化無法完成的情況下,比如接口返回想xml,而這個xml 經常變動,並沒有一個指定的規律,這個時候我們就不能用linq to xml 等反序列化對象了。這個時候就應當使用反射了。

2.真正開發過程中,反射不是是向上面這么寫的,真正的反射是使用組件來完成的,一般也不會使用程序集反射,除非這個框架的某個功能模塊更新頻繁,我們可以使用不同的反射區完成,只需要在xml 文件中配置下就可以了。

3.在這里簡單介紹下組件反射,不是說開發過程中不會有程序集等反射,而是大多數的情況下組件反射就已經能滿足我們的需求了,如AutoFac組件,等其他的。

4.反射技術點一般對應的技術點有 IOC 翻轉,依賴倒置,依賴注入等

下邊分享一篇文章,之所以寫本文,就是因為下邊這篇文文章介紹的太主流,很多人不會使用,Autofac是net core 2.0里面的組件,請看下邊的文章

  文章鏈接1 Autofac 解釋第一個例子 《第一篇》

  文章鏈接2 Autofac 組件、服務、自動裝配 《第二篇》

  文章鏈接3 通過配置的方式Autofac 《第三篇》

以上三篇合起來,我們稱為IOC 設計模式

 

 


免責聲明!

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



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