首先反射是基於System.Reflection命名空間下,.Net框架提供的幫助類庫,可以讀取並使用metadata(元數據:描述對象信息的數據).
我們再來看下代碼生成編譯的總過程。
編譯器編譯(一次編譯):類庫生成的都是dll,控制檯生成的是exe文件
dll和exe都包含兩大塊metadata和IL(中間語言)
dll和exe的運行環境依賴於CLR,我們安裝.net frmework時會自動配置CLR和JIT(及時編譯器)
JIT(二次編譯(編譯之後,可以在不同平臺使用))會將dll和exe文件轉爲機器碼
反射基礎
三種加載dll程序集的方法
//根據dll名稱加載,不帶dll後綴 Assembly assembly = Assembly.Load("Bussiness"); //完整路徑加載,注意web層要引用加載的Bussiness層,如果沒有依賴項,使用時會報錯 Assembly assembly1 = Assembly.LoadFile(@"E:\反射\Rel\bin\Debug\netcoreapp3.1\Bussiness.dll"); //根據dll名稱加載,帶dll後綴 Assembly assembly2 = Assembly.Load("Bussiness.dll"); //獲取該dll中的模塊信息 foreach (var item in assembly.GetModules()) { Console.WriteLine(item.FullyQualifiedName); // => E:\反射\Rel\bin\Debug\netcoreapp3.1\Bussiness.dll } //獲取該dll中的類型信息 foreach (var item in assembly.GetTypes()) { Console.WriteLine(item.FullName); // => Bussiness.Class1 // => Bussiness.Method }
這是Bussiness層信息
根據反射創建對象
//加載程序集 Assembly assembly3 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly3.GetType("Common.StringHelper"); //創建對象 object o = Activator.CreateInstance(type);
Common層內容
當我們創建對象時,會打印 StringHelper被構造了! 因爲每一個類都 自帶一個構造函數,間接執行了WriteLine語句。
但是當我們調用QueryString方法時就報錯了
原因是編譯器不認可,編譯器只把他當成是一個onject對象。
我們可以這樣寫
//加載程序集 Assembly assembly3 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly3.GetType("Common.StringHelper"); //創建對象 dynamic o = Activator.CreateInstance(type); o.QueryString("今天真熱");
dynamic會避開編譯器的檢查,相當於弱語言類型,從而達到我們的預想.
對此我們可以加以封裝一下
public static StringHelper CreateHelper() { //加載程序集 Assembly assembly3 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly3.GetType("Common.StringHelper"); //創建對象 dynamic o = Activator.CreateInstance(type); return o; }
//=>調用
/*
StringHelper stringHelper = Factory.CreateHelper();
await stringHelper.QueryString("開空調");
*/
反射破壞單例:單例類在內存中是唯一的,本來是不能實例化,但是可通過反射來創建對象,從而調用其私有構造函數。
public sealed class Single { private Single() { Console.WriteLine($"{this.GetType().Name}已被構造"); } private static Single single = null; public static Single CreateInstance() { if (single == null) { single = new Single(); } return single; } }
//加載程序集 Assembly assembly3 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly3.GetType("Common.Single"); //創建對象 Common.Single single = (Common.Single)Activator.CreateInstance(type, true);
根據參數調用不同的構造函數
public class TestClass { public TestClass(string parameter) { Console.WriteLine($"{this.GetType().Name}已被構造"); } public TestClass(int parameter) { Console.WriteLine($"{this.GetType().Name}已被構造"); } }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly4.GetType("Common.TestClass"); //創建對象 這裏有一個參數數組,會根據參數的順序和類型匹配對應的構造函數,如果參數是空數組或null,則構造函數不接受任何參數(無參數構造函數) Common.TestClass single = (Common.TestClass)Activator.CreateInstance(type, 1); Common.TestClass single1 = (Common.TestClass)Activator.CreateInstance(type, "1");
反射操作泛型
public class GenericClass<T, R, E> { public void Show(T t, R r, E e) { Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name}"); } }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 注意:`3是佔位符,該泛型對象中有幾個泛型參數 Type type = assembly4.GetType("Common.GenericClass`3"); //不能像這樣創建對象,因爲泛型對象在實例化的時候要指定參數及類型 //object o = Activator.CreateInstance(type); //指定泛型對象的參數類型 Type type1 = type.MakeGenericType(new Type[] { typeof(decimal), typeof(Guid), typeof(Enum) }); object oGeneric = Activator.CreateInstance(type1);
調用無參方法
public class ReflectionTest { public void show1() { Console.WriteLine($"調用了{this.GetType()}中的方法show1"); } }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly4.GetType("Common.ReflectionTest"); //創建對象 object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("show1"); //調用方法,第一個參數是實例類型,第二個是參數列表 methodInfo.Invoke(o, null);
有參方法的調用
public void show2(int i) { Console.WriteLine($"調用了{this.GetType()}中的方法show2"); }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly4.GetType("Common.ReflectionTest"); //創建對象 object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("show2"); //第一個參數是實例類型,第二個是參數列表 methodInfo.Invoke(o, new object[] { 1 });
靜態方法的調用
public static void show3(int i) { Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show3"); }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly4.GetType("Common.ReflectionTest"); //創建對象 object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("show3"); //第一個參數是實例類型,第二個是參數列表 //因爲是靜態方法,所以不需要傳實例對象(可傳可不傳),靜態方法是可以通過方法名直接訪問的 methodInfo.Invoke(null, new object[] { 1 });
重載方法的調用
public static void show4() { Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4"); } public static void show4(int i) { Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4"); } public static void show4(string s) { Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4"); } public static void show4(string s, int i) { Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4"); }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly4.GetType("Common.ReflectionTest"); //創建對象 object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("show4", new Type[] { }); //第一個參數是實例類型,第二個是參數列表 //因爲是靜態方法,所以不需要傳實例對象(可傳可不傳),靜態方法是可以通過方法名直接訪問的 methodInfo.Invoke(null, new object[] { }); { MethodInfo methodInfo1 = type.GetMethod("show4", new Type[] { typeof(int) }); methodInfo1.Invoke(null, new object[] { 1 }); } { MethodInfo methodInfo2 = type.GetMethod("show4", new Type[] { typeof(string) }); methodInfo2.Invoke(null, new object[] { "1" }); } { MethodInfo methodInfo3 = type.GetMethod("show4", new Type[] { typeof(string), typeof(int) }); methodInfo3.Invoke(null, new object[] { "1", 1 }); }
私有方法的調用
private void show5(string s) { Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show5"); }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 Type type = assembly4.GetType("Common.ReflectionTest"); //創建對象 object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("show5", BindingFlags.Instance | BindingFlags.NonPublic); //第一個參數是實例類型,第二個是參數列表 methodInfo.Invoke(o, new object[] { "1" });
調用泛型類中的方法
public void Show(T t, R r, E e) { Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name}"); }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 注意:`3是佔位符,該泛型對象中有幾個泛型參數 Type type = assembly4.GetType("Common.GenericClass`3"); //指定泛型對象的參數類型 Type type1 = type.MakeGenericType(new Type[] { typeof(double), typeof(Guid), typeof(DateTime) }); object oGeneric = Activator.CreateInstance(type1); MethodInfo methodInfo = type1.GetMethod("Show"); methodInfo.Invoke(oGeneric, new object[] { 1.02, Guid.NewGuid(), DateTime.Now });
調用泛型類中的泛型方法
public class GenericClass<T, R, E> { public void Show<X>(T t, R r, E e, X x) { Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name},{x.GetType().Name}"); } }
//加載程序集 Assembly assembly4 = Assembly.Load("Common"); //獲取指定類型對象的(類型)信息 注意:`3是佔位符,該泛型對象中有幾個泛型參數 Type type = assembly4.GetType("Common.GenericClass`3"); //指定泛型對象的參數類型 Type type1 = type.MakeGenericType(new Type[] { typeof(double), typeof(Guid), typeof(DateTime) }); object oGeneric = Activator.CreateInstance(type1); MethodInfo methodInfo = type1.GetMethod("Show");
//指定泛型方法的參數類型 MethodInfo methodInfo1 = methodInfo.MakeGenericMethod(new Type[] { typeof(string) }); methodInfo1.Invoke(oGeneric, new object[] { 1.2, Guid.NewGuid(), DateTime.Now, "" });
反射操作實體字段、屬性
public class People { public People() { Console.WriteLine($"{this.GetType().FullName}構造了一次"); } public int id { get; set; } public string name { get; set; } public string description { get; set; } }
Type type = typeof(People); object o = Activator.CreateInstance(type); //type.GetFields()也可以,操作也是一樣的 foreach (var item in type.GetProperties()) { //Console.WriteLine(item.Name);//打印字段屬性名 //Console.WriteLine(item.GetValue(o));//打印字段值 if (item.Name.Equals("id")) item.SetValue(o, 1);//設置字段值 if (item.Name.Equals("name")) item.SetValue(o, "111"); Console.WriteLine($"{type.Name}.{item.Name}={item.GetValue(o)}"); }
反射映射字段,對象中字段數據轉換賦值
public class People { public People() { Console.WriteLine($"{this.GetType().FullName}構造了一次"); } public int id { get; set; } public string name { get; set; } public string description { get; set; } } public class DtoPeople { public DtoPeople() { Console.WriteLine($"{this.GetType().FullName}構造了一次"); } public int id { get; set; } public string name { get; set; } public string description { get; set; } }
People people = new People(); people.id = 1; people.name = "1"; people.description = ""; Type typePeople = typeof(People); Type typeDtoPeople = typeof(DtoPeople);
//需要賦值的對象 object o = Activator.CreateInstance(typeDtoPeople); foreach (var item in typeDtoPeople.GetFields()) { //找出源類型字段的值 item.Name爲字段屬性名 object value = typePeople.GetProperty(item.Name).GetValue(people); //賦值 item.SetValue(o, value); }
反射的基礎就寫到這兒,接下來幾篇會利用反射寫自定義ORM與IOC組件.
反射的優點:動態獲取對象.可擴展
反射的缺點:1.寫起來複雜.2.避開編譯器的檢查,錯誤風險很大.3.性能不好
bye-bye.