|
1.What?反射是什么? 反射:無處不在,MVC,Webfrom,asp.net,ORM,IOC,AOP,幾乎所有的框架都離不開反射,那么反射到底是什么? 我們寫的代碼,計算機要識別,需要二次編譯,中間會經(jīng)過編譯器編譯,得到dll,exe,再被JIT編譯最終被計算機語言識別,執(zhí)行,那dll,exe是怎么生成的呢?
生成的exe還可以直接打開執(zhí)行
那這個exe里面具體是什么呢?我們可以用反編譯工具打開看看,打開就可以看到我們反編譯以后的IL:也是一種面向?qū)ο笳Z言,但是不太好閱讀
反射Reflection:system.Reflection,其實就是.net.fromwork提供的一個幫助內(nèi)庫,可以讀取并使用metadata 那我們?nèi)绾稳?/span>讀取信息呢? 2.用反射加載和讀取信息[三種方式]我們先隨便寫個接口類:新建-DB.Interface類庫,新建一個接口類IDBHelper 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DB.Interface 8 { 9 // DB.Interface接口類庫 10 public interface IDBHelper 11 { 12 void Query(); 13 } 14 } 再新建一個DB.MySql類庫,新建一個MySqlHelper類繼承接口IDBHelper
using DB.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DB.MySql { public class MySqlHelper : IDBHelper { /// <summary> /// MySqlHelper構(gòu)造函數(shù) /// </summary> public MySqlHelper() { Console.WriteLine("{0}被構(gòu)造", this.GetType().Name); } /// <summary> /// Query方法 /// </summary> public void Query() { Console.WriteLine("{0}.Query",this.GetType().Name); } } } 按照上面的步驟,新建一個DB.SqlServer類庫,新建一個SqlServerHelper類繼承接口IDBHelper
using DB.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DB.SqlServer { /// <summary> /// SqlServer實現(xiàn) /// </summary> public class SqlServerHelper : IDBHelper { /// <summary> /// SqlServerHelper構(gòu)造函數(shù) /// </summary> public SqlServerHelper() { Console.WriteLine("{0}被構(gòu)造",this.GetType().Name); } /// <summary> /// Query方法 /// </summary> public void Query() { Console.WriteLine("{0}.Query",this.GetType().Name); } } } #region Reflecgion { Console.WriteLine("---------------------------------------------反射加載和讀取信息---------------------------------------------------------"); //動態(tài)加載 都是把dll加到內(nèi)存里面去,不需要向上面的普通方法一樣引用 //1.一個完整的dll名稱,不需要后綴,目的:把dll加載到內(nèi)存中間去; //缺陷:但是有個限制從編譯以后生成的exe所在的路徑去查找,既可以找dll,又可以找exe,兩個一樣得話先找dll【常用】 Assembly assembly = Assembly.Load("DB.MySql"); //2.還有一種加載方式,path,需要一個完整的路徑,性能差不多,也可以切換為別的引用了的路徑【全名稱= 全路徑+dll名稱 + 后綴】 Assembly assembly1 = Assembly.LoadFile(@"F:\LearnTest\MyReflectionFirst\MyReflectionFirst\bin\Debug\DB.MySql.dll"); //3.loadfrom:帶后綴的,當前路徑查找,也可以全路徑 Assembly assembly2 = Assembly.LoadFrom("DB.MySql.dll");//【當前路徑+ 后綴】 Assembly assembly3 = Assembly.LoadFrom(@"F:\LearnTest\MyReflectionFirst\MyReflectionFirst\bin\Debug\DB.MySql.dll"); //讀取里面的信息 foreach (var types in assembly.GetTypes())//GetTypes 獲取此程序集中的全部類型 { Console.WriteLine(types.Name);
我們跟蹤就可以看到利用反射加載出來的一些基本信息 3.反射使用信息#region Common { Console.WriteLine("---------------------------------------------Common---------------------------------------------------------"); { //添加接口引用,內(nèi)庫引用,實例化并且調(diào)用方法 IDBHelper dBHelper = new MySqlHelper(); dBHelper.Query(); } } #endregion #region Reflecgion { Console.WriteLine("---------------------------------------------用反射使用信息---------------------------------------------------------"); //1.動態(tài)加載 Assembly assembly = Assembly.Load("DB.MySql"); // 第一步我們會獲取到全部的類型,也可以指定獲取類型【完整類型名稱】 Type type = assembly.GetType("DB.MySql.MySqlHelper"); //3.實例化 // 意思跟Activator.CreateInstance(type);差不多都是實例化new MySqlHelper();但是這邊創(chuàng)建的實例是個object類型 object oDBHelper=Activator.CreateInstance(type); //很確定oDBHelper里面有Query這個方法Why不能直接調(diào)用方法?編譯器不認可,不能直接調(diào)用 //c#是一門強類型語言,靜態(tài)語言,編譯時就確定好了類型確保安全 //oDBHelper.Query() //dynamic可以是因為它很特殊編譯器不檢查,運行時才檢查 //dynamic dDBHelper = Activator.CreateInstance(type); //dDBHelper.Query(); //4.類型轉(zhuǎn)換,類型不對會直接返回null IDBHelper dBHelper = oDBHelper as IDBHelper; //5.方法調(diào)用 dBHelper.Query(); } #endregion 那么問題來了我只需要用普通方法兩句話寫完的,我們?yōu)槭裁匆梅瓷湟獙?句???何必呢?意義何在? 我們就上面的代碼先來封裝一下看看: 1 <configuration> 2 <startup> 3 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> 4 </startup> 5 <appSettings> 6 <!--以英文逗號","分開--> 7 <add key="IDBHelperConfig" value="DB.MySql.MySqlHelper,DB.MySql"/> 8 </configuration> namespace MyReflectionFirst { public class SingleFactory { /// <summary> /// private避免被人可以看到,static靜態(tài),會在程序第一次調(diào)用的時候讀取,只讀一遍 /// </summary> private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelperConfig"].ToString(); private static string TypeName = IDBHelperConfig.Split(',')[0]; private static string DLLName = IDBHelperConfig.Split(',')[1]; public static IDBHelper CreateInstance() { Assembly assembly = Assembly.Load(DLLName); Type type = assembly.GetType(TypeName); object oDBHelper = Activator.CreateInstance(type); IDBHelper dBHelper = oDBHelper as IDBHelper; return dBHelper; } } } 那我們再來調(diào)用看看: { Console.WriteLine("-------------------------------------Reflecgion+Factory+Config-------------------------------------------------"); IDBHelper dBHelper = SingleFactory.CreateInstance(); dBHelper.Query(); } 這是普通方法和反射封裝完成調(diào)用 的結(jié)果以及代碼對比,從調(diào)用上是不是差不多了呢?
我們再進一步對比延伸擴展一下: 如果我們需要更換一下版本呢? 普通方法:【就必須重新引用,修改代碼,重新編譯發(fā)布】 //如果從MySqlHelper換成SqlServerHelper,就必須重新引用,修改代碼,重新編譯發(fā)布 IDBHelper dBHelper = new SqlServerHelper(); dBHelper.Query(); 而我們的反射,只需修改配置文件即可: <add key="IDBHelperConfig" value="DB.SqlServer.SqlServerHelper,DB.SqlServer"/> 結(jié)果: 那要是我們要新增一個以前沒有的Oracle版本: 按照上面的步驟,新建一個DB.Oracle類庫,新建一個OracleHelper類繼承接口IDBHelper
using DB.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DB.Oracle { public class OracleHelper : IDBHelper { public OracleHelper() { Console.WriteLine("{0}被構(gòu)造",this.GetType().Name); } public void Query() { Console.WriteLine("{0}.Query",this.GetType().Name); } } }
用反射,通過修改配置文件就可以達到以下結(jié)果: <add key="IDBHelperConfig" value="DB.Oracle.OracleHelper,DB.Oracle"/> 結(jié)果: //特點:動態(tài)擴展性太強 //可配置可擴展:完全不修改原有代碼,只是增加新的實現(xiàn),copy,修改配置文件,就可以支持新功能 //原理:反射的動態(tài)加載和動態(tài)創(chuàng)建對象,以及配置文件結(jié)合 //可以支持隨機換,也可以后面新增 //前提:實現(xiàn)類必須是事先已有的,而且在目錄下面的 4.反射的使用上面我們用反射獲取了無參數(shù)的構(gòu)造函數(shù),那么我們怎么去獲取有參數(shù)的構(gòu)造函數(shù)呢?我們怎么去獲取構(gòu)造函數(shù)參數(shù),以及類型,調(diào)用? 讓我們看一個例子,在DB.SqlServer類庫中,新建一個ReflecgionTest類 public class ReflecgionTest { #region Identity public ReflecgionTest() { Console.WriteLine("這是{0}無參數(shù)構(gòu)造函數(shù)", this.GetType()); } public ReflecgionTest(string name) { Console.WriteLine("這是{0}有參數(shù)構(gòu)造函數(shù)", this.GetType()); } public ReflecgionTest(int id) { Console.WriteLine("這是{0}有參數(shù)構(gòu)造函數(shù)", this.GetType()); } #endregion } { Console.WriteLine("-----------------------------------ctor¶meter---------------------------------------------------"); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.ReflecgionTest"); //獲取構(gòu)造函數(shù) foreach (ConstructorInfo ctor in type.GetConstructors())//循環(huán)訪問集合或數(shù)組的名稱 { Console.WriteLine(ctor.Name); //獲取構(gòu)造函數(shù)參數(shù) foreach (var parameter in ctor.GetParameters()) { //獲取構(gòu)造函數(shù)參數(shù)類型 Console.WriteLine(parameter.ParameterType); } } //怎么為不同的構(gòu)造函數(shù)指定不同的參數(shù)類型 //object test1 = Activator.CreateInstance(type); //object test2 = Activator.CreateInstance(type, "123"); //object test3 = Activator.CreateInstance(type, 123); //應用建議改為以下寫法 object test4 = Activator.CreateInstance(type, new object[] { "123" }); object test5= Activator.CreateInstance(type, new object[] { 123}); } 結(jié)果: 5.反射-黑科技:反射破壞單例讓我們看一個例子,在DB.SqlServer類庫中,新建一個Singleton類 namespace DB.SqlServer { /// <summary> /// 單例模式:就是一個類,保證在整個進程中就只有一個實例 /// </summary> public sealed class Singleton { //3.當然這個實例只能有一個,所以我們提供了一個靜態(tài)字段, private static Singleton _singleton = null; //1.構(gòu)造函數(shù)私有化,才不能隨便new,才能保證只有一個對象 private Singleton() { Console.WriteLine("{0}被構(gòu)造",this.GetType().Name); } //4.這個字段只能初始化一次,那怎么初始化?我們交給了這個靜態(tài)構(gòu)造函數(shù),它由CLR保障,在程序啟動的第一次調(diào)用這個類Singleton之前完成的調(diào)用,而且只調(diào)用一次 static Singleton() { _singleton = new Singleton(); } //2.私有化了之后,對外不能new,但我可以對外提供一個公開的靜態(tài)方法,負責提供這個對象的實例,可以調(diào)用這個方法獲取這個實例,那怎么獲??? public static Singleton GetInstance() { return _singleton; } } } { Console.WriteLine("-----------------------------------擴展:未使用反射之前的單例調(diào)用---------------------------------------------------"); //Singleton singleton = new Singleton();//私有化不能直接構(gòu)造 Singleton singleton1 = Singleton.GetInstance(); Singleton singleton2 = Singleton.GetInstance(); Singleton singleton3 = Singleton.GetInstance(); Console.WriteLine($"{Object.ReferenceEquals(singleton1, singleton3)}");//不管有幾個實例,結(jié)果肯定為True,因為都是同一個對象,只會被構(gòu)造一次 } { Console.WriteLine("-----------------------------------擴展:反射破壞單例---反射調(diào)用私有構(gòu)造函數(shù)---------------------------------------------------"); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.Singleton"); //Singleton singletonA=(Singleton)Activator.CreateInstance(type);//類型轉(zhuǎn)換 //如上寫法會報錯,因為上面我們說過它相當于一個實例化,但是這個構(gòu)造函數(shù)私有化,不能直接New Singleton singletonA = (Singleton)Activator.CreateInstance(type,true);//但是反射做到了這件事 //Why這個地方執(zhí)行了第一次使用的時候會執(zhí)行兩遍構(gòu)造函數(shù),程序在一次使用這個類的時候就會去完成靜態(tài)構(gòu)造函數(shù)的構(gòu)造,程序在每次使用都會再去構(gòu)造一次 Singleton singletonB = (Singleton)Activator.CreateInstance(type, true); Singleton singletonC = (Singleton)Activator.CreateInstance(type, true); //nonPublic:如果公共或非公共默認構(gòu)造函數(shù)可以匹配,則為true;如果只有公共默認構(gòu)造函數(shù)可以匹配,則為false。 //所以對象是不一樣的,對象被構(gòu)造了多次 Console.WriteLine($"{Object.ReferenceEquals(singletonA,singletonC)}"); } 結(jié)果對比: 六.反射調(diào)用泛型類與方法讓我們看一個例子,在DB.SqlServer類庫中,新建一個GenericTest類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DB.SqlServer { public class GenericTest<T,W,X> { public void Show(T t, W w,X x) { Console.WriteLine("This's t.type={0},w.type={1}, x.type={2}", t.GetType().Name,w.GetType().Name,x.GetType().Name); } } public class GenericMethod { public void Show<T, W, X>(T t, W w, X x) { Console.WriteLine("This's t.type={0},w.type={1}, x.type={2},t:{3},w:{4},x:{5}", t.GetType().Name, w.GetType().Name, x.GetType().Name, t, w, x); } } public class GenericDouble<T> { public void Show<W, X>(T t, W w, X x) { Console.WriteLine("This's t.type={0},w.type={1}, x.type={2},t:{3},w:{4},x:{5}", t.GetType().Name, w.GetType().Name, x.GetType().Name,t,w,x); } } } { Console.WriteLine("-----------------------------------擴展:反射和泛型---------------------------------------------------"); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.GenericTest"); Object oGeneric = Activator.CreateInstance(type); } 按照之前的操作,我們發(fā)現(xiàn)這邊得到的type=null?Why?
//原因:因為public class GenericTest<T,W,X>它是泛型編譯之后會變?yōu)镈B.SqlServer.GenericTest`3<T, W, X>【反編譯工具也可以看到】
// 我們需要把它修改為: Type type = assembly.GetType("DB.SqlServer.GenericTest`3"); 跟蹤結(jié)果已經(jīng)找到了我們要的數(shù)據(jù)了:
再執(zhí)行,報錯了,類型找到了,為什么它不能創(chuàng)建這個實例?它不是有個無參數(shù)構(gòu)造函數(shù)嗎?為什么沒能創(chuàng)建一個實例?
那這代碼現(xiàn)在應該如何修改呢? //在泛型類型時,不能忘記我們創(chuàng)建對象,它是不能夠?qū)嵗?【eg:GenericTest genericTest=new GenericTest()】那肯定是不行的 //起碼你得指定好類型才能夠?qū)嵗緀g:GenericTest<int, string, DateTime> genericTest = new GenericTest<int, string, DateTime>();】那應該怎樣寫呢? Type typeMake = type.MakeGenericType(new Type[] {typeof(int),typeof(string),typeof(DateTime) }); //泛型類是一個不確定類型的,這句話就表示我把類型確定一下,你要什么參數(shù)類型,這邊是個type數(shù)組,new Type[] {}數(shù)組 Object oGeneric = Activator.CreateInstance(typeMake);//只有這個typeMake才可以創(chuàng)建,這邊不能強制轉(zhuǎn)換,因為需要類型 跟蹤的結(jié)果:
如果反射創(chuàng)建對象之后,知道方法名稱,怎么樣不做類型轉(zhuǎn)換,直接調(diào)用方法?讓我們直接下面的實例吧
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DB.SqlServer { public class ReflecgionTest { #region Identity public ReflecgionTest() { Console.WriteLine("這是{0}無參數(shù)構(gòu)造函數(shù)", this.GetType()); } public ReflecgionTest(string name) { Console.WriteLine("這是{0}有參數(shù)構(gòu)造函數(shù)", this.GetType()); } public ReflecgionTest(int id) { Console.WriteLine("這是{0}有參數(shù)構(gòu)造函數(shù)", this.GetType()); } #endregion #region Method /// <summary> /// 無參方法 /// </summary> public void Show1() { Console.WriteLine("這里是{0}的Show1", this.GetType()); } /// <summary> /// 有參數(shù)方法 /// </summary> /// <param name="id"></param> public void Show2(int id) { Console.WriteLine("這里是{0}的Show2", this.GetType()); } /// <summary> /// 重載方法之一 /// </summary> /// <param name="id"></param> /// <param name="name"></param> public void Show3(int id, string name) { Console.WriteLine("這里是{0}的Show3,id:{1},string:{2}", this.GetType(),id,name); } /// <summary> /// 重載方法之二 /// </summary> /// <param name="name"></param> /// <param name="id"></param> public void Show3(string name, int id) { Console.WriteLine("這里是{0}的Show3_2,string:{1},id:{2}", this.GetType(),name,id); } /// <summary> /// 重載方法之三 /// </summary> /// <param name="id"></param> public void Show3(int id) { Console.WriteLine("這里是{0}的Show3_3,id:{1}", this.GetType(),id); } /// <summary> /// 重載方法之四 /// </summary> /// <param name="name"></param> public void Show3(string name) { Console.WriteLine("這里是{0}的Show3_4,name:{1}", this.GetType(),name); } /// <summary> /// 重載方法之五 /// </summary> public void Show3() { Console.WriteLine("這里是{0}的Show3_1", this.GetType()); } /// <summary> /// 私有方法 /// </summary> /// <param name="name"></param> private void Show4(string name) { Console.WriteLine("這里是{0}的Show4_私有方法調(diào)用", this.GetType()); } /// <summary> /// 靜態(tài)方法 /// </summary> /// <param name="name"></param> public static void Show5(string name) { Console.WriteLine("這里是{0}的Show5_靜態(tài)方法調(diào)用", typeof(ReflecgionTest)); } #endregion } } 反射調(diào)用:無參數(shù),有參數(shù),重載,靜態(tài),私有方法實例{ Console.WriteLine("如果反射創(chuàng)建對象之后,知道方法名稱,怎么樣不做類型轉(zhuǎn)換,直接調(diào)用方法?"); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.ReflecgionTest"); Object test = Activator.CreateInstance(type); //主要是查看它里面的所有方法名,參數(shù),參數(shù)類型 foreach (var metohd in type.GetMethods()) { Console.WriteLine(metohd.Name+":"); foreach (var parameter in metohd.GetParameters()) { Console.WriteLine($"{parameter.Name},{parameter.ParameterType.Name}"); } } //反射調(diào)用無參數(shù)的方法 { MethodInfo methodInfo = type.GetMethod("Show1"); methodInfo.Invoke(test, null);//第一個參數(shù)表示實例,后一個參數(shù)列表 } //反射調(diào)用帶參數(shù)的方法 { MethodInfo methodInfo1 = type.GetMethod("Show2"); methodInfo1.Invoke(test, new object[] { 123 }); } //重載方法調(diào)用 { MethodInfo method=type.GetMethod("Show3",new Type[] { typeof(string),typeof(int)}); method.Invoke(test, new object[] { "現(xiàn)在是哪一年", 2020}); } { MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(int),typeof(string)}); method.Invoke(test, new object[] { 2020, "year" }); } { MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(int)}); method.Invoke(test, new object[] { 2020 }); } { MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(string)}); method.Invoke(test, new object[] { "wang" }); } { MethodInfo method = type.GetMethod("Show3",new Type[] { }); method.Invoke(test, null); } //靜態(tài)方法【特別】:實例可以要,也可以不要【null】 { MethodInfo method = type.GetMethod("Show5"); method.Invoke(null,new object[] { "你好"}); } { MethodInfo method = type.GetMethod("Show5"); method.Invoke(test, new object[] { "你好" }); } {//反射調(diào)用私有方法 var method = type.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic); method.Invoke(test, new object[] { "私有方法調(diào)用" }); } } 結(jié)果:
那么泛型的方法又如何用反射調(diào)用呢?{ Console.WriteLine("-----------------------------------擴展:泛型方法GenericMethod---------------------------------------------------"); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.GenericMethod"); Object oGeneric = Activator.CreateInstance(type); MethodInfo method = type.GetMethod("Show");//泛型方法不用Show`3 var methodnew=method.MakeGenericMethod(new Type[]{ typeof(int),typeof(string), typeof(DateTime)}); methodnew.Invoke(oGeneric, new object[] { 12, "泛型方法 ", DateTime.Now }); } 結(jié)果: 泛型方法和泛型類同時存在的情況:{ Console.WriteLine("擴展:泛型類+泛型方法GenericMethod"); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.GenericDouble`1").MakeGenericType(new Type[] { typeof(int) }); Object oObject=Activator.CreateInstance(type); MethodInfo method = type.GetMethod("Show").MakeGenericMethod(new Type[] {typeof(string),typeof(DateTime) }); method.Invoke(oObject,new object[] { 2020,"hello",DateTime.Today}); } 結(jié)果: 七:反射的應用:獲取全部的實體的全部屬性并給其賦值: //實體類不考慮擴展問題,沒意義 Console.WriteLine("---------------------------------------------Reflection---------------------------------------------------------"); Type type = typeof(People); Object opeople=Activator.CreateInstance(type); //獲取全部的屬性 foreach (var prop in type.GetProperties()) { Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(opeople)}"); //給prop里面的屬性增加一個值 if (prop.Name.Equals("Id")) { prop.SetValue(opeople, 1); } else if(prop.Name.Equals("Name")) { prop.SetValue(opeople, "泛型實體屬性"); } Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(opeople)}"); } foreach (var field in type.GetFields()) { Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(opeople)}"); if (field.Name.Equals("Description")) { field.SetValue(opeople, "只是一個字段"); } Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(opeople)}"); } //思考:反射對實體層的操作如此麻煩,Why我們還要用它?eg:讓我們一起看一個場景: public Company FindOld(int Id) { string sql = @"SELECT [Id] ,[Name] ,[CreateTime] ,[CreatorId] ,[LastModifierId] ,[LastModifyTime] FROM [Company] where Id="+Id; //[Name],[CreateTime],[CreatorId],[LastModifierId],[LastModifyTime],[Id] using (SqlConnection con = new SqlConnection(ConnectionStringCustomers)) { SqlCommand sqlCommand = new SqlCommand(sql, con); con.Open(); var reader=sqlCommand.ExecuteReader(); if (reader.Read())//有數(shù)據(jù)才開始讀,有數(shù)據(jù)True { Console.WriteLine(reader[1]); } con.Close(); con.Dispose(); } return new Company(); } 現(xiàn)在只有一個表,我們還有一個表,那如果我們有很多表,那豈不是都需要寫很多方法, 如果我們希望一個方法,能返回不同的類型,針對以上代碼優(yōu)化: /// <summary> /// 如果我們希望一個方法,能返回不同的類型?泛型+反射【不同類型,不同sql】, /// </summary> public T Find<T>(int Id) { Type type = typeof(T); //type.GetProperties().Select(p=> p.Name)得到屬性名稱, //type.GetProperties().Select(p => $"[{p.Name}]")得到它的屬性名稱并寫出如下格式:[CreatorId]*/ //得到的是一個集合,需要把它變成一個逗號連接的字符串 //得到它的屬性名稱,把它變成一個逗號連接的字符串 string prop = string.Join(",", type.GetProperties().Select(p => $"[{p.Name}]")); //動態(tài)拼接Sql string sql = $"select {prop} from [{type.Name}] where ID={Id}"; Object oObject = Activator.CreateInstance(type); //如果是User表User在數(shù)據(jù)庫里面屬于關(guān)鍵字,[{type.Name}]類似FROM [User] using (SqlConnection con = new SqlConnection(ConnectionStringCustomers)) { SqlCommand sqlCommand = new SqlCommand(sql, con); con.Open(); var reader = sqlCommand.ExecuteReader(); if (reader.Read())//有數(shù)據(jù)才開始讀,有數(shù)據(jù)True { //獲取所有的屬性進行賦值 foreach (var propinfo in type.GetProperties()) { //針對數(shù)據(jù)庫名跟實力類名一一對應的場景 propinfo.SetValue(oObject, reader[propinfo.Name] is DBNull ? null : reader[propinfo.Name]); } return (T)oObject; } else { return default; } } } 調(diào)用: { SqlServerHelper sqlServer = new SqlServerHelper(); Company company = sqlServer.Find<Company>(1); User user = sqlServer.Find<User>(1); } { Console.WriteLine("---------------------------------------------Common---------------------------------------------------------"); People people = new People(); people.Id = 1; people.Name = "普通"; people.Description = "描述:"; Console.WriteLine($"people.Id ={people.Id}"); Console.WriteLine("people.Name = {0},people.Description={1}",people.Name,people.Description); } //Get:反射展示是意義的,可以不用改代碼,普通方法需要修改 //Set:感覺沒什么用,但是還是有用, //反射: //優(yōu)點:動態(tài),【幾乎任何一個框架里面都有反射,我們搭建框架就是希望有良好的擴展性,能夠應對將來任意的需求變化】 //缺點;1.使用麻煩【封裝一下】;2.避開編譯器檢查,運行時出問題;3.性能問題;
using DB.Interface; using DB.SqlServer; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace MyReflectionFirst { public class Monitor { public static void Show() { Console.WriteLine("------------------------------Monitor普通方法與反射的性能測試--------------------------------------------"); //初始化 long ComonTime = 0; long ReflectionTime = 0; { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 1_000_000; i++) { IDBHelper sqlServer = new SqlServerHelper(); sqlServer.Query(); } stopwatch.Stop(); ComonTime = stopwatch.ElapsedMilliseconds; } { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Assembly assembly = Assembly.Load("DB.SqlServer"); Type type = assembly.GetType("DB.SqlServer.SqlServerHelper"); for (int i = 0; i < 1_000_000; i++) { Object oDBHelper = Activator.CreateInstance(type); IDBHelper dBHelper = oDBHelper as IDBHelper; dBHelper.Query(); } stopwatch.Stop(); ReflectionTime = stopwatch.ElapsedMilliseconds; } Console.WriteLine("ComonTime:{0}ms,ReflectionTime:{1}ms", ComonTime, ReflectionTime); //ComonTime:71ms,ReflectionTime:134ms } } }
|
|
|