什么是反射
反射總結(jié)目錄
什么是反射
程序運行時將exe、dll文件加載到內(nèi)存并執(zhí)行一些操作的過程,這個過程稱為反射。
通常所說的反射是實現(xiàn)這個過程所使用的技術手段,.Net中System.Reflection等命名空間提供了反射的實現(xiàn)。反射的原理
通過對程序集元數(shù)據(jù)的搜索找到對應的成員類型并使用,以實現(xiàn)驗證或動態(tài)調(diào)用程序集的目的。一個簡單的例子引入反射
下面這簡單例子引入反射的使用,這個例子中定義了一個Hello類并添加一個Say方法,我將使用反射調(diào)用Say方法。
namespace ReflectionStudy {public class Hello{public void Say(){Console.WriteLine("Hello Reflection!");}} }//使用反射技術調(diào)用Say方法 //1.從當前程序集中查找Hello類 var helloType= Assembly.GetExecutingAssembly().GetType("ReflectionStudy.Hello"); //2.獲取Hello類的Say方法 var method = helloType.GetMethod("Say"); //3.創(chuàng)建Hello類的實例 var helloInstance=Activator.CreateInstance(helloType); //4.執(zhí)行Say方法 method.Invoke(helloInstance, null);System.Reflection命名空間
上面的例子雖然簡單,但是已足夠說明反射的大致流程:
1. 加載程序集
因為程序集是個比較大的概念,而這偏離了這篇文章的主題,請移步我的另一篇文章《程序集》。
2. 發(fā)現(xiàn)類型
FCL提供了許多API來獲取程序集中的類型,目前常用的API是Assembly中的ExportedTypes、DefinedTypes、GetType等,ExportedTypes屬性用來獲取公開方法即public類型,DefiedTypes屬性用來獲取所有類型,GetType方法獲取一個指定的類型。
var assembly = Assembly.Load(@"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); int index=0; Console.WriteLine("獲取程序集:{0} 中的ExportedTypes",assembly); foreach (var type in assembly.ExportedTypes) {Console.WriteLine("{0}. {1}", ++index,type); } Console.WriteLine("ExportedTypes類型有{0}個",assembly.ExportedTypes.Count()); index = 0; Console.WriteLine("獲取程序集:{0} 中的DefinedTypes", assembly); foreach (var type in assembly.DefinedTypes) {Console.WriteLine("{0}. {1}", ++index, type); } Console.WriteLine("DefinedTypes類型有{0}個", assembly.ExportedTypes.Count());3. 構(gòu)造類型實例
在FCL中提供了幾個構(gòu)造類型實例的機制分散在System.Activator,System.AppDomain,System.Reflection.ConstructorInfo中,如果查看源碼的話可以看到內(nèi)部實現(xiàn)調(diào)用的都是Activator.CreateInstance。
下面的例子演示實例的創(chuàng)建 public class Hello {public void Say(){Console.WriteLine("Hello Reflectioin!");} } //出于演示的目的下面這句簡化了加載程序集的步驟 Type helloType = typeof(Hello); //創(chuàng)建實例 var hello=Activator.CreateInstance(helloType);4. 發(fā)現(xiàn)類型成員
在FCL中有反射提供了一個類型基類System.Reflection.MemberInfo,其派生類如下圖所示:
通常使用Type類型來發(fā)現(xiàn)成員類型如:Method,Filed,Property,Event等。
.Net 4中可以通過Type.GetTypeInfo擴展方法獲取TypeInfo對象以便獲取更多功能,相比Type類型TypeInfo代價更高。
下面通過例子來說明如何發(fā)現(xiàn)成員:
成員類型描述: * 一個事件OnSay * 兩個字段,_name是private,Age是Public * 一個Name屬性 * 三個方法,SayHello是Static public,Say是public,HaHa是private using System;namespace ReflectionAssembly {public class Hello{//事件public event Action OnSay;//私有字段private string _name;//共有字段public string Age;//屬性public string Name{get{return _name;}set{_name = value;}}//靜態(tài)方法public static void SayHello(){Console.WriteLine("SayHello");}//實例方法public void Say(){Console.WriteLine("Hello Reflectioin!");}//測試事件public void TestEvent(){if (OnSay != null){Console.WriteLine($"OnSay綁定了{OnSay.GetInvocationList().Length}個方法");//遍歷執(zhí)行綁定的事件foreach (Action onSay in OnSay.GetInvocationList()){onSay();}}}//私有實例方法void HaHa(){Console.WriteLine("HaHa");}} }因為MemberInfo是所有成員類型的基類,那么我們先看一下獲取所有的成員類型:
static void Main(string[] args) {var helloType = Assembly.Load("ReflectionAssembly").GetType("ReflectionAssembly.Hello");var memberInfos = helloType.GetMembers();foreach (var memberInfo in memberInfos){Console.WriteLine($"成員類型:{memberInfo.MemberType}\t類型名稱:{memberInfo.Name}");}Console.WriteLine($"類型{helloType.FullName}共{memberInfos.Count()}個成員");Console.Read(); }//運行結(jié)果如下: 成員類型:Method 類型名稱:add_OnSay 成員類型:Method 類型名稱:remove_OnSay 成員類型:Method 類型名稱:get_Name 成員類型:Method 類型名稱:set_Name 成員類型:Method 類型名稱:SayHello 成員類型:Method 類型名稱:Say 成員類型:Method 類型名稱:ToString 成員類型:Method 類型名稱:Equals 成員類型:Method 類型名稱:GetHashCode 成員類型:Method 類型名稱:GetType 成員類型:Constructor 類型名稱:.ctor 成員類型:Property 類型名稱:Name 成員類型:Event 類型名稱:OnSay 成員類型:Field 類型名稱:Age 類型ReflectionAssembly.Hello共14個成員Why?我們只定義了7個成員,結(jié)果卻顯示14個?
- 如果你了解Event類型那么可以忽略add_OnSay,remove_OnSay?!妒录臀袑W習總結(jié)》
- 如果你了解Property那么可以忽略get_Name,set_Name。
- 如果你了解Object那么可以忽略ToString,Equals,GetHasCode,GetType。(所有的引用類型都繼承Object)
- 如果你知道默認構(gòu)造函數(shù)那么可以忽略.ctor。
- 現(xiàn)在剩下的OnSay,Age,Name,Say,SayHello5個類是我們定義的,還有2個_name,Haha沒有在上面運行結(jié)果中顯示,我們發(fā)現(xiàn)這個成員有個共同點是:他們都是私有成員。
如何獲取私有成員
我們將代碼稍作改動看看效果:
GetMembers,FindMembers內(nèi)部實現(xiàn)
Type類型同時也提供FindMembers方法來獲取成員類型,如果觀察其內(nèi)部實現(xiàn)會發(fā)現(xiàn)它們僅僅是一個包裝方法, 它們通過包裝GetMethods,GetFields,GetProperties,GetEvents,GetConstructors方法的實現(xiàn)來獲取所有的成員信息。以下方法均提供了多個重載方法
發(fā)現(xiàn)字段(FildInfo)
helloType.GetFields();發(fā)現(xiàn)屬性(PropertyInfo)
helloType.GetProperties();發(fā)現(xiàn)方法(MethodInfo)
helloType.GetMethods();發(fā)現(xiàn)構(gòu)造器(ConstructorInfo)
helloType.GetConstructors();發(fā)現(xiàn)事件(EventInfo)
helloType.GetEvents();5. 執(zhí)行對類的操作
調(diào)用字段
字段類型通過GetValue,SetValue方法對來操作
//實例化Hello var hello = Activator.CreateInstance(helloType); //調(diào)用private字段 var _name = helloType.GetField("_name",BindingFlags.NonPublic|BindingFlags.Instance); _name.SetValue(hello, "guodf"); Console.WriteLine(_name.GetValue(hello)); //調(diào)用public字段 var age = helloType.GetField("Age"); age.SetValue(hello, "16"); Console.WriteLine(age.GetValue(hello));調(diào)用屬性
屬性類型通過get_**,set_**方法來操作
//實例化Hello var hello = Activator.CreateInstance(helloType); //調(diào)用屬性 var name = helloType.GetProperty("Name"); name.SetValue(hello, "guodf test"); Console.WriteLine($"_name:{_name.GetValue(hello)}\tName:{name.GetValue(hello)}");調(diào)用方法
方法通過Invoke執(zhí)行方法
//調(diào)用實例方法 var say = helloType.GetMethod("Say"); say.Invoke(hello,null); //調(diào)用private實例方法 var haha = helloType.GetMethod("HaHa", BindingFlags.Instance | BindingFlags.NonPublic); haha.Invoke(hello, null);調(diào)用事件
//調(diào)用事件 var testEvent = helloType.GetMethod("TestEvent"); var onSay = helloType.GetEvent("OnSay"); Action event1 = () => { Console.WriteLine("event1"); }; Action event2 = () => { Console.WriteLine("event2"); }; //綁定2個方法 onSay.AddEventHandler(hello, event1); onSay.AddEventHandler(hello, event2); testEvent.Invoke(hello,null); //移除一個方法 onSay.RemoveEventHandler(hello, event1); testEvent.Invoke(hello,null);調(diào)用靜態(tài)方法
var sayHello=hello.GetMethod("SayHello"); sayHello.Invoke(null, null);隱式反射和顯式反射
C#中有隱式轉(zhuǎn)換和顯式轉(zhuǎn)換得概念,通常派生類轉(zhuǎn)換為基類型被稱為隱私轉(zhuǎn)換,因為可以直接將派生類型賦值給基類型;反之稱為顯示轉(zhuǎn)換。那么在反射得使用過程中,我通常使用兩種實現(xiàn)方式來使用反射對象:一種基于接口的編程方式,另一種則是完全的字符串查找方式。所以我將基于接口的方式稱為顯式反射,這種做法的好處是編程期間我們可以直接使用類型的方法;而另一種基于字符串找好的方式我稱它為隱式反射,因為在使用過程中無論得到那種成員類型都是通過字符串查找實現(xiàn)的。
反射的優(yōu)缺點
優(yōu)點
1. 動態(tài)加載,按需加載 2. 解耦缺點
1. 無編譯期類型安全檢查 2. 性能低小結(jié)
反射一種技術,這種技術可以幫助我們實現(xiàn)一些看起來很酷的編程設計,但這種技術并不完美,它犧牲了效率換來了靈活性。至于這種犧牲的價值當然是仁者見仁智者見智。
轉(zhuǎn)載于:https://www.cnblogs.com/guodf/p/6585566.html
總結(jié)
- 上一篇: bzoj 3277 串 后缀树+子树不
- 下一篇: Codeforces Round #40