在一線編碼已有多年,積累了不少非常實用的技能,最近的更新會逐步的分享出來,希望能幫助到還有一丟丟喜歡.Net的朋友,當然這些都比較適合入門選手,雖然自己已是個精通抄代碼的老猿,但技術造詣仍是渣渣。
猶記得當年,自己憑借滿腔熱血,習得一身Java理論知識,一本《Java從入門到精通》常伴左右。初入大四后,已覺自己羽翼豐滿,可以起飛,於是躍躍欲試,自信滿滿的外出找實習。我拿着自己精心制作的簡歷,上面一眾“圖書管理系統”、“學生成績查詢系統”、“酒店管理系統”、“出入庫管理系統”等熱血參與大制作。想着自己擁有如此豐厚的經歷,offer定是信手拈來。
第一家:五人大公司,深藏居民樓小角落
大胡子:你知道PHP嗎?
我:……我想學java
大胡子:PHP現在是最流行的語言,我們有專人帶你,就看中你的好學。
我:可是我想學java
大胡子:給你一個月開1600,怎么樣?
我:好(真是毫無原則的狗蛋)……
一禮拜后我離職了,他們哪里是在做開發,就是做拼接頁面而已,我也只是整理資料,打掃衛生。
第二家:10人超大公司,一間公寓
小白臉:做過公司系統嗎?
我:(難道我做的都是玩的嗎?)做的少。
小白臉:做學生成績查詢系統時如何考慮並發?
我:……
小白臉:問了你幾個問題,都是理論多,實操很少啊。
我:……
小白臉:給你個建議,別着急找工作,回去再好好學,基礎不扎實么,要多做公司級系統。
我:……
第三家:15人巨頭公司,居民樓
老板:我們現在願意招學生,願意培養,java和.net都一樣,條條大路通羅馬,不用過於追求語言的差別,學好了都是大牛。
我:是的是的(被一語道破心中疑慮,反正我小白一個,用什么技術棧都一樣從零起步)
老板:來我們公司,我帶你……
一如此門深似海,從此Java是路人。
--------以上演義都是本人真實經歷改編,意在告誡各位語言無好壞,只有使用的人才有差別
我們來看下今天的主題:
聽到反射,很多人應該和我一樣有這么幾個疑問:
1.DLL內容都了解的話,直接引用DLL不就好了嗎,為什么還要反射?
2.DLL里面的內容什么都不知道的話,就算反射的話,也不知道里面的方法是干什么的啊,和直接引用DLL沒區別啊?
這幾個問題先不着急回答,我們繼續分析下。
想要知道反射,就必須先了解一下計算機是如何運行我們寫的代碼的,如下圖:
對於計算機來講,它只認識01010101之類的二進制代碼,人類寫的高級語言(如C#、JAVA等)計算機是沒法識別的,所以需要將高級語言轉化為01讓計算機可以識別的二進制編碼,中間是有一個過程的。就拿C#來講,VS編譯器會將編寫好的代碼進行編譯,編譯后會生成exe/dll文件,.Net Core里面已經不生成exe了,都是dll。dll和exe還需要CLR/JIT的即時編譯成字節碼,才能最終被計算機執行。有伙伴就會問為什么要編譯2次呢,先編譯到dll,再編譯到字節碼01呢,為什么不能一次性編譯成字節碼呢?因為我們寫的是C#語言,但是真實運行的機器有很多種,可能是32位,也可能是64位,操作系統可能是windows、linux、unix等,不同的計算機不同的操作系統識別字節碼的可能是不一樣的,但是從高級語言編譯成exe/dll這一步是一樣的。所以只要在不同運行環境的計算機上安裝對應的不同的CLR/JIT,就可以運行我們同一個exe/dll了。這里就大概講下這樣一個過程,后面會有章節詳細講解程序如何被計算機執行的。現在我們先關注編譯生成的exe/dll,它包含2部分,分別是中間語言IL和源數據元數據metadata。IL里面包含我們寫的大量的代碼,比如說方法、實體類等。元數據metadata不是我們寫的代碼,它是編譯器在編譯的時候生成的描述,它可能是把命名空間、類名、屬性名記錄了一下,包括特性。
講上面程序的編譯過程跟反射有什么關系呢?我們反射就是讀取metadata里面的數據的,然后去使用它。
反射是.NET中的重要機制,通過反射可以得到*.exe或*.dll等程序集內部的接口、類、方法、字段、屬性、特性等信息,還可以動態創建出類型實例並執行其中的方法。
一、反射的用途:
類型 | 作用 |
---|---|
Assembly | 定義和加載程序集,加載程序集清單中列出的模塊,以及從此程序集中查找類型並創建該類型的實例。 |
Module | 了解包含模塊的程序集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。 |
ConstructorInfo | 了解構造器的名稱、參數、訪問修飾符(如public或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法來調用特定的構造函數。 |
MethodInfo | 了解方法的名稱、返回類型、參數、訪問修飾符(如public或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法來調用特定的方法。 |
FieldInfo | 了解字段的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,並獲取或設置字段值。 |
EventInfo | 了解事件的名稱、事件處理程序數據類型、自定義特性、聲明類型和反射類型等,並添加或移除事件處理程序。 |
PropertyInfo | 了解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,並獲取或設置屬性值。 |
ParameterInfo | 了解參數的名稱、數據類型、參數是輸入參數還是輸出參數等,以及參數在方法簽名中的位置等。 |
二、反射實例
我們通過實際例子來看下反射的用途。
1.首先建立一個控制台程序,並添加一個類庫,里面建立一個AnimalsInfo類
AnimalsInfo中定義如下屬性和方法:
public class AnimalsInfo { public string Type { get; set; } public int Size { get; set; } public void CommonMethod() { Console.WriteLine("我就是一個普通方法"); } public void ParameterMethod(string type) { Console.WriteLine("我是帶參數方法,我是" + type); } public void OverrideMethod(int size) { Console.WriteLine($"我是重載方法,我有{size}大"); } public void OverrideMethod(string name) { Console.WriteLine("我是重載方法,我叫" + name); } public void GenericityMethod<T>(T t) { Console.WriteLine("我是泛型方法方法,類型是" + typeof(T)); } private void PrivateMethod() { Console.WriteLine("我是私有方法"); } public static void StaticMethod() { Console.WriteLine("我是靜態方法"); } }
2.利用反射獲取類庫,屬性
using System; //第一步引用命名空間 using System.Reflection; namespace ReflectionTest { class Program { static void Main(string[] args) {
Console.WriteLine("以下是獲取類庫的"); //第二步,動態加載類庫,一定寫要獲取類庫的**絕對路徑** Assembly assembly = Assembly.LoadFile(@"C:\Users\XA-BAU-Lyvin\source\repos\ReflectionTest\ReflectionTest.Model\bin\Debug\netcoreapp3.1\ReflectionTest.Model.dll"); //第三步,動態獲取類型,寫類庫的名稱和類的名稱 Type type = assembly.GetType("ReflectionTest.Model.AnimalsInfo"); Console.WriteLine(type.Name);
Console.WriteLine("以下是獲取屬性的");
//遍歷類型的屬性集合
foreach (var item in type.GetProperties())
{
Console.WriteLine("字段名:"+ item.Name + ",類型:" + item.PropertyType);
}
}
}
}
3.通過反射獲取方法
- 所有的方法都要指定要獲取的方法名稱
- 創建方法,第一個參數,對象,第二個參數,是一個object對象數組,寫對應的參數類型
- 私有方法不一樣,一定有看清楚,它指明是父類的私有方法
static void Main(string[] args) { Console.WriteLine("以下是獲取類庫的"); //第二步,動態加載類庫,一定寫要獲取類庫的**絕對路徑** Assembly assembly = Assembly.LoadFile(@"C:\Users\XA-BAU-Lyvin\source\repos\ReflectionTest\ReflectionTest.Model\bin\Debug\netcoreapp3.1\ReflectionTest.Model.dll"); //第三步,動態獲取類型,寫類庫的名稱和類的名稱 Type type = assembly.GetType("ReflectionTest.Model.AnimalsInfo"); Console.WriteLine(type.Name); Console.WriteLine("以下是獲取屬性的"); //遍歷類型的屬性集合 foreach (var item in type.GetProperties()) { Console.WriteLine("字段名:"+ item.Name + ",類型:" + item.PropertyType); } Console.WriteLine("==============普通方法=================="); //創建一個符合類型的對象 object oAnimal = Activator.CreateInstance(type); //***所有的方法都要指定要獲取的方法名稱 MethodInfo commonMethod = type.GetMethod("CommonMethod"); //創建方法,第一個參數,對象,第二個參數,沒有則為空 commonMethod.Invoke(oAnimal, null); Console.WriteLine("==============帶參數的方法=================="); MethodInfo parameterMethod = type.GetMethod("ParameterMethod"); //創建方法,第一個參數,對象,第二個參數,是一個object對象數組,寫對應的參數類型 parameterMethod.Invoke(oAnimal, new object[] { "狗狗" }); Console.WriteLine("==============重載方法int參數=================="); MethodInfo overrideMethodInt = type.GetMethod("OverrideMethod", new Type[] { typeof(int) }); overrideMethodInt.Invoke(oAnimal, new object[] { 18 }); Console.WriteLine("==============重載方法string參數=================="); MethodInfo overrideMethodStrint = type.GetMethod("OverrideMethod", new Type[] { typeof(string) }); overrideMethodStrint.Invoke(oAnimal, new object[] { "喵喵" }); Console.WriteLine("==============泛型方法=================="); MethodInfo genericityMethod = type.GetMethod("GenericityMethod").MakeGenericMethod(new Type[] { typeof(int) }); genericityMethod.Invoke(oAnimal, new object[] { 45 }); Console.WriteLine("==============私有方法=================="); //指定要獲取的方法名稱,指明是父類的私有方法 MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.Instance | BindingFlags.NonPublic); privateMethod.Invoke(oAnimal, null); Console.WriteLine("==============靜態方法================="); MethodInfo staticMethod = type.GetMethod("StaticMethod"); staticMethod.Invoke(null, null); }
三、總結
所有的反射應用方法都已經講完了,看完以后感覺其實也沒有什么神秘的,很簡單對不對?
當然,還有個問題要留給大家繼續討論了:如果通過反射還可以訪問私有方法,那么設置私有方法的意義在哪呢?是否和私有類型的設計初衷違背了?
首發自:【程序員不帥哥 】公眾號
原文鏈接:https://mp.weixin.qq.com/s/LCPLjBmmbJwXBDWdi3SU1g
掃碼關注,更多精彩內容及時獲取,一起提高,一起加油