什麼是反射,反射(Reflection)是.NET中的重要機制,微軟提供的一個幫助類庫,命名空間System.Reflection,主要類型System.Type。通過反射,可以在運行時獲得.NET中每一個類型(包括類、結構、委託、接口和枚舉等)的成員,包 括方法、屬性、事件,以及構造函數等。常見的反射: MVC AOP IOC ORM
本文主要是記錄在回味學習過程中接收到的一些反射的使用,主要包含了:反射的幾種方式、反射使用,反射的擴展封裝(程序可配置)、通過反射選擇不同的構造函數創建對象、反射的調用
反射擴展,訪問私有,破壞單例模式(可以調用私有函數,每次都被創建新的對象)、反射調用泛型、反射使用屬性字段等實例。
因會設計到反射的使用,所以先貼項目結構和代碼。此處只是簡單的項目的項目結構,用於更好的查看效果。
實例項目結構
1、項目結構
1、DB.Interface
1 namespace DB.Interface 2 { 3 public interface IDBHelper 4 { 5 void Query(); 6 } 7 }
2、DB.SqlServer
1 using System 2 /// <summary> 3 /// 反射調用泛型 4 /// </summary> 5 namespace DB.SqlServer 6 { 7 /// <summary> 8 /// 類中泛型方法 9 /// </summary> 10 public class GenericMethod 11 { 12 public void show<T, W, X>(T t, W w, X x) 13 { 14 Console.WriteLine("t.Type={0},w.Type={1},x.Type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name); 15 } 16 } 17 18 /// <summary> 19 /// 泛型類中泛型方法 20 /// </summary> 21 /// <typeparam name="T"></typeparam> 22 /// <typeparam name="W"></typeparam> 23 /// <typeparam name="X"></typeparam> 24 public class GenericTest<T, W, X> 25 { 26 public void show(T t, W w, X x) 27 { 28 Console.WriteLine("t.Type={0},w.Type={1},x.Type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name); 29 } 30 } 31 32 33 34 public class GenericDouble<T> 35 { 36 public void show<W, X>(T t, W w, X x) 37 { 38 Console.WriteLine("t.Type={0},w.Type={1},x.Type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name); 39 } 40 } 41 }
1 using System; 2 3 namespace DB.SqlServer 4 { 5 public class RefectionTest 6 { 7 /// <summary> 8 /// 無參 9 /// </summary> 10 public RefectionTest() { 11 Console.WriteLine("這裏是{0}無參構造函數",this.GetType()); 12 } 13 14 /// <summary> 15 /// 帶參string 16 /// </summary> 17 public RefectionTest(string name) 18 { 19 Console.WriteLine("這裏是{0}有參構造函數string", this.GetType()); 20 } 21 22 /// <summary> 23 /// 帶參int 24 /// </summary> 25 public RefectionTest(int id) 26 { 27 Console.WriteLine("這裏是{0}有參構造函數int", this.GetType()); 28 } 29 30 /// <summary> 31 /// 無參 帶參 32 /// </summary> 33 public void RefectionTestShow() { 34 Console.WriteLine("這裏是{0}的Show1無參",this.GetType()); 35 } 36 37 public void RefectionTestShow1(string name) 38 { 39 Console.WriteLine("這裏是{0}的Show1帶參string", this.GetType()); 40 } 41 42 public void RefectionTestShow2(int id,string name) 43 { 44 Console.WriteLine("這裏是{0}的Show1帶參int", this.GetType()); 45 } 46 47 /// <summary> 48 /// 重載開始 49 /// </summary> 50 /// <param name="id"></param> 51 public void RefectionTestShow3(int id) 52 { 53 Console.WriteLine("這裏是{0}的RefectionTestShow3帶參int", this.GetType()); 54 } 55 public void RefectionTestShow3(int id, string name) 56 { 57 Console.WriteLine("這裏是{0}的Show1帶參int,string", this.GetType()); 58 } 59 60 /// <summary> 61 /// 私有方法 62 /// </summary> 63 /// <param name="id"></param> 64 private void RefectionTestShow4(int id) 65 { 66 Console.WriteLine("這裏是{0}RefectionTestShow4私有", this.GetType()); 67 } 68 69 /// <summary> 70 /// 靜態方法 71 /// </summary> 72 /// <param name="id"></param> 73 public static void RefectionTestShow5(int id) 74 { 75 Console.WriteLine("這裏是{0}RefectionTestShow5",typeof(RefectionTest)); 76 } 77 } 78 }
1 using System; 2 3 namespace DB.SqlServer 4 { 5 /// <summary> 6 /// 單例 7 /// </summary> 8 public sealed class Singleton 9 { 10 private static Singleton _singleton = null; 11 private Singleton() 12 { 13 Console.WriteLine("Singleton被構造"); 14 } 15 16 static Singleton() 17 { 18 _singleton = new Singleton(); 19 } 20 21 public static Singleton GetInstance() 22 { 23 return _singleton; 24 } 25 } 26 }
1 using DB.Interface; 2 using Model; 3 using System; 4 using System.Collections.Generic; 5 using System.Configuration; 6 using System.Data.SqlClient; 7 using System.Linq; 8 using System.Text; 9 using System.Threading.Tasks; 10 11 namespace DB.SqlServer 12 { 13 public class SqlServerHelper : IDBHelper 14 { 15 private static string Customers = ""; 16 public SqlServerHelper() 17 { 18 Console.WriteLine("{0}被構造", this.GetType().Name); 19 } 20 public void Query() 21 { 22 Console.WriteLine("{0}.Query", this.GetType().Name); 23 } 24 25 /// <summary> 26 /// 數據庫查詢 27 /// </summary> 28 /// <param name="Id"></param> 29 /// <returns></returns> 30 public static Company GetCompanyById(int Id) 31 { 32 string sql = @"SELECT [Id] 33 ,[Name] 34 ,[CreateTime] 35 ,[CreatorId] 36 ,[LastModifierId] 37 ,[LastModiyTime] 38 FROM [Customers].[dbo].[Company] WHERE id=" + Id; 39 Type type = typeof(Company); 40 object oCompany = Activator.CreateInstance(type); 41 using (SqlConnection connection = new SqlConnection(Customers)) 42 { 43 SqlCommand sqlCommand = new SqlCommand(sql, connection); 44 connection.Open(); 45 SqlDataReader reader = sqlCommand.ExecuteReader(); 46 if (reader.Read()) 47 { 48 //foreach (var prop in type.GetProperties()) 49 //{ 50 // if (prop.Name.Equals("Id")) 51 // prop.SetValue(oCompany, reader["Id"]); 52 // if (prop.Name.Equals("Name")) 53 // prop.SetValue(oCompany, reader["Name"]); 54 //} 55 foreach (var prop in type.GetProperties()) 56 { 57 prop.SetValue(oCompany, reader[prop.Name]); 58 } 59 60 } 61 } 62 63 return (Company)oCompany; 64 } 65 66 67 public static T GetCompanyById<T>(int Id) where T : BaseModel//約束 68 { 69 70 Type type = typeof(T); 71 object oCompany = Activator.CreateInstance(type); 72 var propNames = type.GetProperties().Select(s => $"[{s.Name}]"); 73 string props = string.Join(",", propNames); 74 string sql = $"SELECT {props} FROM [{type.Name}] WHERE id={Id}"; 75 using (SqlConnection connection = new SqlConnection(Customers)) 76 { 77 SqlCommand sqlCommand = new SqlCommand(sql, connection); 78 connection.Open(); 79 SqlDataReader reader = sqlCommand.ExecuteReader(); 80 if (reader.Read()) 81 { 82 foreach (var prop in type.GetProperties()) 83 { 84 prop.SetValue(oCompany, reader[prop.Name]); 85 } 86 87 } 88 } 89 90 return (T)oCompany; 91 } 92 } 93 }
3、Model
1 namespace Model 2 { 3 public class BaseModel 4 { 5 public int Id { get; set; } 6 } 7 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Model 8 { 9 public class Company : BaseModel 10 { 11 public string Name{ get; set; } 12 13 public DateTime CreateTime { get; set; } 14 15 public int CreatorId { get; set; } 16 17 public int? LastModifierId { get; set; } 18 } 19 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Model 8 { 9 public class People 10 { 11 public People() { 12 Console.WriteLine("{0}被創建", this.GetType().FullName); 13 } 14 15 public int Id { get; set; } 16 public string Name { get; set; } 17 public string Description; 18 } 19 }
4、MyReflection_Net
1 using DB.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Reflection; 6 using System.Text; 7 using System.Threading.Tasks; 8 using System.Configuration; 9 10 namespace MyReflection_Net 11 { 12 public class SimlpFactory 13 { 14 private static string IDBSqlHelperConfig = ConfigurationManager.AppSettings["IDBSqlHelperConfig"];//配置 15 private static string typeName = IDBSqlHelperConfig.Split(',')[0];//類名稱 16 private static string dllName = IDBSqlHelperConfig.Split(',')[1];//DLL名稱 17 public static IDBHelper CreateInstentce() 18 { 19 //1、動態加載 20 Assembly assembly = Assembly.Load(dllName); 21 Type type = assembly.GetType(typeName); 22 //3、創建對象 23 object oDBHelper = Activator.CreateInstance(type); 24 //4、類型轉換 25 IDBHelper iDBHelper = oDBHelper as IDBHelper;//此方式類型如果不匹配就會返回一個NULL 26 return iDBHelper; 27 } 28 } 29 }
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <startup> 4 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> 5 </startup> 6 <appSettings> 7 <!--配置反射DLL,第一個指DLL中需要反射的類,第二個是DLL名稱--> 8 <add key="IDBSqlHelperConfig" value="DB.SqlServer.SqlServerHelper,DB.SqlServer"/> 9 </appSettings> 10 <connectionStrings> 11 <!--數據庫連接字符串配置--> 12 <add name="Customers" connectionString=""></add> 13 </connectionStrings> 14 </configuration>
反射的幾種方式
1 Assembly assembly = Assembly.Load("DB.SqlServer"); //DLL/EXE名稱(默認當前目錄查找) 2 Assembly assembly1 = Assembly.LoadFile(@"R:\MyReflection_Net\bin\Debug\DB.SqlServer.dll");//全名稱/完整物理路徑 3 Assembly assembly2 = Assembly.LoadFrom(@"R:\MyReflection_Net\bin\Debug\DB.SqlServer.dll");//帶後綴(可以全名稱,可以默認當前目錄查找) 4 //輸出 //獲取對象 5 foreach (Type type in assembly.GetTypes()) 6 { 7 Console.WriteLine("assembly:" + type.Name); 8 foreach (MethodInfo method in type.GetMethods()) 9 { 10 Console.WriteLine("method" + method.Name); 11 } 12 }
反射的使用
1 //1、動態加載 2 Assembly assembly = Assembly.Load("DB.SqlServer"); //DLL/EXE名稱(默認當前目錄查找) 3 //2、獲取類型 4 Type type = assembly.GetType("DB.SqlServer.SqlServerHelper");//命名空間+類名 5 //3、創建對象 6 object oDBHelper = Activator.CreateInstance(type); 7 //4、類型轉換 8 //注:由於C#是強類型語言,所以不能直接調用方法,如:oDBHelper.Query(),會被編譯器不認可,所以需要用到dynamic(動態類型,可以避開編譯器檢查,由於避開編譯器檢查,此方式會存在安全問題,導致無法使用) 9 //dynamic dDBHerper = Activator.CreateInstance(type); 10 // dDBHerper.Query(); 11 IDBHelper iDBHelper = oDBHelper as IDBHelper;//此方式類型如果不匹配就會返回一個NULL 12 //5、調用方法 13 iDBHelper.Query();
反射的封裝調用
我們在這裏可以對反射的調用進行封裝,創建了SimlpFactory類,並在App.Config中對需要反射調用的DLL或EXE進行配置。這樣做的好處是:
1、編寫代碼的時候工作量小
2、遇到業務變更或者新的業務時不需要重新編譯和發佈,通過配置可解決,同時也減輕了工作量
IDBHelper iDBherper = SimlpFactory.CreateInstentce();
iDBherper.Query();
通過反射選擇不同的構造函數創建對象
1 //1、動態加載 2 Assembly assembly = Assembly.Load("DB.SqlServer"); //DLL/EXE名稱(默認當前目錄查找) 3 //2、獲取類型 4 Type type = assembly.GetType("DB.SqlServer.RefectionTest");//命名空間+類名 5 //3、創建對象 6 object oDBHelper = Activator.CreateInstance(type); 7 object oDBHelper1 = Activator.CreateInstance(type, new object[] { "傑克" }); 8 object oDBHelper2 = Activator.CreateInstance(type, new object[] { "我是誰" }); 9 object oDBHelper3 = Activator.CreateInstance(type, new object[] { 123 });
反射不同方法、參數的調用
1 Assembly assembly = Assembly.Load("DB.SqlServer"); 2 Type type = assembly.GetType("DB.SqlServer.RefectionTest"); 3 object otest = Activator.CreateInstance(type); 4 //1、無參數 5 MethodInfo show = type.GetMethod("RefectionTestShow"); 6 show.Invoke(otest, new object[0]);//反射調用方法 7 //2、有參數(int) 8 MethodInfo show1 = type.GetMethod("RefectionTestShow1"); 9 show1.Invoke(otest, new object[] { "123" });//反射調用方法,參數類型需要和方法參數類型保持一致 10 //3、多參數 11 MethodInfo show2 = type.GetMethod("RefectionTestShow2", new Type[] { typeof(int), typeof(string) }); 12 show2.Invoke(otest, new object[] { 1, "123" });//多個參數 13 //4、重載 14 MethodInfo show3 = type.GetMethod("RefectionTestShow3", new Type[] { typeof(int), typeof(string) }); 15 show3.Invoke(otest, new object[] { 1, "123" });///多個參數 16 //5、調用私有方法 17 MethodInfo show4 = type.GetMethod("RefectionTestShow4", BindingFlags.NonPublic | BindingFlags.Instance); 18 show4.Invoke(otest, new object[] { 1 }); 19 //6、調用靜態 20 MethodInfo show5 = type.GetMethod("RefectionTestShow5"); 21 show5.Invoke(otest, new object[] { 1 });//反射調用靜態方法,對象的實例可以傳入,也可不傳入(null) 22 show5.Invoke(null, new object[] { 2 });
反射擴展,訪問私有(可以調用私有函數,每次都被創建新的對象)
1 Singleton singleton1 = Singleton.GetInstance(); 2 Singleton singleton2 = Singleton.GetInstance(); 3 Console.WriteLine(singleton1.Equals(singleton2)); 4 Assembly assembly = Assembly.Load("DB.SqlServer"); 5 Type type = assembly.GetType("DB.SqlServer.Singleton"); 6 object oSingleton1 = Activator.CreateInstance(type, true);//參數true:訪問私有的函數 7 object oSingleton2 = Activator.CreateInstance(type, true); 8 Console.WriteLine(oSingleton1.Equals(oSingleton2));
反射調用泛型 泛型編譯後需要生成佔位符如: `1 `2 ` 3
1 //原始方式 2 // GenericMethod genericMethod = new GenericMethod(); 3 // genericMethod.show<int, string, DateTime>(123,"",DateTime.Now); 4 //反射 5 //類中調用泛型方法 6 Assembly assembly = Assembly.Load("DB.SqlServer"); 7 Type type = assembly.GetType("DB.SqlServer.GenericMethod");//獲取方法的時候不需要佔位符 8 object oTest = Activator.CreateInstance(type); 9 MethodInfo genericMethod = type.GetMethod("show"); 10 MethodInfo genericMethod1 = genericMethod.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) });//指定方法的具體參數 11 genericMethod1.Invoke(oTest, new object[] { 123, "GenericMethod", DateTime.Now }); 12 //泛型類中調用泛型方法 13 Assembly assembly1 = Assembly.Load("DB.SqlServer"); 14 Type typeGenericMethod = assembly.GetType("DB.SqlServer.GenericTest`3");//需要佔位符 ,GenericTest<T, W, X>三個佔位符所以是`3 15 Type typeGenericMethod1 = typeGenericMethod.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) }); 16 object oGenericMethod = Activator.CreateInstance(typeGenericMethod1); 17 MethodInfo genericMethod1_1 = typeGenericMethod1.GetMethod("show"); 18 genericMethod1_1.Invoke(oGenericMethod, new object[] { 234, "GenericTest", DateTime.Now });
反射使用屬性、字段
1 //八、反射使用屬性字段 2 //1、獲取類型 3 Type type = typeof(People); 4 //創建對象 5 object oPeople = Activator.CreateInstance(type); 6 //字段 7 foreach (PropertyInfo prop in type.GetProperties()) 8 { 9 if (prop.Name.Equals("Id")) 10 prop.SetValue(oPeople, 123); 11 if (prop.Name.Equals("Name")) 12 prop.SetValue(oPeople, "Name"); 13 Console.WriteLine($"name:" + prop.Name + "=" + prop.GetValue(oPeople)); 14 } 15 //屬性 16 foreach (FieldInfo field in type.GetFields()) 17 { 18 if (field.Name.Equals("Description")) 19 field.SetValue(oPeople, "Description"); 20 Console.WriteLine($"name:" + field.Name + "=" + field.GetValue(oPeople)); 21 }
最後簡單的對比一下我們在日常工作中使用反射和不使用反射的區別,此處用調用數據庫的Helper做實例
1 public static Company GetCompanyById(int Id) 2 { 3 string sql = @"SELECT [Id] 4 ,[Name] 5 ,[CreateTime] 6 ,[CreatorId] 7 ,[LastModifierId] 8 ,[LastModiyTime] 9 FROM [Customers].[dbo].[Company] WHERE id=" + Id; 10 Type type = typeof(Company); 11 object oCompany = Activator.CreateInstance(type); 12 using (SqlConnection connection = new SqlConnection(Customers)) 13 { 14 SqlCommand sqlCommand = new SqlCommand(sql, connection); 15 connection.Open(); 16 SqlDataReader reader = sqlCommand.ExecuteReader(); 17 if (reader.Read()) 18 { 19 //此處是對比字段,可優化成第二種 20 //第一種 21 //foreach (var prop in type.GetProperties()) 22 //{ 23 // if (prop.Name.Equals("Id")) 24 // prop.SetValue(oCompany, reader["Id"]); 25 // if (prop.Name.Equals("Name")) 26 // prop.SetValue(oCompany, reader["Name"]); 27 //} 28 //第二種 29 foreach (var prop in type.GetProperties()) 30 { 31 prop.SetValue(oCompany, reader[prop.Name]); 32 } 33 34 } 35 } 36 37 return (Company)oCompany; 38 }
1 public static T GetCompanyById<T>(int Id) where T : BaseModel//約束 2 { 3 4 Type type = typeof(T); 5 object oCompany = Activator.CreateInstance(type); 6 var propNames = type.GetProperties().Select(s => $"[{s.Name}]"); 7 string props = string.Join(",", propNames); 8 string sql = $"SELECT {props} FROM [{type.Name}] WHERE id={Id}"; 9 using (SqlConnection connection = new SqlConnection(Customers)) 10 { 11 SqlCommand sqlCommand = new SqlCommand(sql, connection); 12 connection.Open(); 13 SqlDataReader reader = sqlCommand.ExecuteReader(); 14 if (reader.Read()) 15 { 16 foreach (var prop in type.GetProperties()) 17 { 18 prop.SetValue(oCompany, reader[prop.Name]); 19 } 20 21 } 22 } 23 24 return (T)oCompany; 25 }
總結
反射特點:
1、動態:減少對象之間的依賴,只需要指導類明、方法名,就可以達到自己調用的目的
2、可以突破特定的權限,可以做到普通方式無法做到的目的,如訪問對象中的私有方法
3、編寫比較困難,識別性不太好,編寫和後期維護相對麻煩
4、由於反射回經過一系列的處理,會導致性能損耗相對差一些, 效率比較:1百萬次差距 普通140ms 反射35000ms,可以通過緩存進行優化,但是依然比普通方式相差10倍以上,所以根據自己的需要來使用