對c# 反射機制的理解

反射是一個程序集發現及運行的過程,通過反射可以得到*.exe或*.dll等程序集內部的信息。使用反射可以看到一個程序集內部的接口、類、方法、字段、屬性、特性等等信息。在System.Reflection命名空間內包含多個反射常用的類,下面表格列出了常用的幾個類。



類型 作用 
Assembly 通過此類可以加載操縱一個程序集,並獲取程序集內部信息 
EventInfo 該類保存給定的事件信息 
FieldInfo 該類保存給定的字段信息 
MethodInfo 該類保存給定的方法信息 
MemberInfo 該類是一個基類,它定義了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多個公用行爲 
Module 該類可以使你能訪問多個程序集中的給定模塊 
ParameterInfo 該類保存給定的參數信息       
PropertyInfo 該類保存給定的屬性信息

一、System.Reflection.Assembly類
     通過Assembly可以動態加載程序集,並查看程序集的內部信息,其中最常用的就是Load()這個方法。
     Assembly assembly=Assembly.Load("MyAssembly");
     利用Assembly的object CreateInstance(string) 方法可以反射創建一個對象,參數0爲類名。
二、System.Type類
     Type是最常用到的類,通過Type可以得到一個類的內部信息,也可以通過它反射創建一個對象。一般有三個常用的方法可得到Type對象。
利用typeof() 得到Type對象
Type type=typeof(Example);
利用System.Object.GetType() 得到Type對象
Example example=new Example();
Type type=example.GetType();
利用System.Type.GetType() 得到Type對象
Type type=Type.GetType("MyAssembly.Example",false,true);
注意參數0是類名,參數1表示若找不到對應類時是否拋出異常,參數1表示類名是否區分大小寫
   例子:
   我們最常見的是利用反射與Activator結合來創建對象。
   Assembly assembly= Assembly.Load("MyAssembly");
   Type type=assembly.GetType("Example");
   object obj=Activator.CreateInstance(type);
三、反射方法
    1.通過 System.Reflection.MethodInfo能查找到類裏面的方法

[csharp] view plain copy
  1. Type type=typeof(Example);  
  2.     MethodInfo[] listMethodInfo=type.GetMethods();  
  3.     foreach(MethodInfo methodInfo in listMethodInfo)  
  4.          Cosole.WriteLine("Method name is "+methodInfo.Name);  


2.我們也能通過反射方法執行類裏面的方法2.我們也能通過反射方法執行類裏面的方法

[csharp] view plain copy
  1. Assembly assembly= Assembly.Load("MyAssembly");  
  2.    Type type=assembly.GetType("Example");  
  3.    object obj=Activator.CreateInstance(type);  
  4.    MethodInfo methodInfo=type.GetMethod("Hello World");  //根據方法名獲取MethodInfo對象  
  5.    methodInfo.Invoke(obj,null);  //參數1類型爲object[],代表Hello World方法的對應參數,輸入值爲null代表沒有參數  


四、反射屬性
   1.通過 System.Reflection.PropertyInfo 能查找到類裏面的屬性
     常用的方法有GetValue(object,object[]) 獲取屬性值和 SetValue(object,object,object[]) 設置屬性值
四、反射屬性
   1.通過 System.Reflection.PropertyInfo 能查找到類裏面的屬性
     常用的方法有GetValue(object,object[]) 獲取屬性值和 SetValue(object,object,object[]) 設置屬性值
四、反射屬性
   1.通過 System.Reflection.PropertyInfo 能查找到類裏面的屬性
     常用的方法有GetValue(object,object[]) 獲取屬性值和 SetValue(object,object,object[]) 設置屬性值

[csharp] view plain copy
  1. Type type=typeof(Example);  
  2.     PropertyInfo[] listPropertyInfo=type.GetProperties();  
  3.     foreach(PropertyInfo propertyInfo in listPropertyInfo)  
  4.          Cosole.WriteLine("Property name is "+ propertyInfo.Name);  


2.我們也可以通過以下方法設置或者獲取一個對象的屬性值2.我們也可以通過以下方法設置或者獲取一個對象的屬性值

[csharp] view plain copy
  1. Assembly assembly=Assembly.Load("MyAssembly");  
  2.   Type type=assembly.GetType("Example");  
  3.   object obj=Activator.CreateInstance(type);  
  4.   PropertyInfo propertyInfo=obj.GetProperty("Name");    //獲取Name屬性對象  
  5.   var name=propertyInfo.GetValue(obj,null);                //獲取Name屬性的值  
  6.   PropertyInfo propertyInfo2=obj.GetProperty("Age");     //獲取Age屬性對象  
  7.   propertyInfo.SetValue(obj,34,null);                              //把Age屬性設置爲34  


五、反射字段
    通過 System.Reflection.FieldInfo 能查找到類裏面的字段
    它包括有兩個常用方法SetValue(object ,object )和GetValue(object)  因爲使用方法與反射屬性非常相似,在此不再多作介紹
   (略)
六、反射特性
   通過System.Reflection.MemberInfo的GetCustomAttributes(Type,bool)就可反射出一個類裏面的特性,以下例子可以反射出一個類的所有特性
五、反射字段
    通過 System.Reflection.FieldInfo 能查找到類裏面的字段
    它包括有兩個常用方法SetValue(object ,object )和GetValue(object)  因爲使用方法與反射屬性非常相似,在此不再多作介紹
   (略)
六、反射特性
   通過System.Reflection.MemberInfo的GetCustomAttributes(Type,bool)就可反射出一個類裏面的特性,以下例子可以反射出一個類的所有特性

[csharp] view plain copy
  1. Type type=typeof("Example");  
  2.    object[] typeAttributes=type.GetCustomAttributes(false);       //獲取Example類的特性  
  3.    foreach(object attribute in typeAttributes)  
  4.          Console.WriteLine("Attributes description is "+attribute.ToString());  


通過下面例子,可以獲取Example類Name屬性的所有特性通過下面例子,可以獲取Example類Name屬性的所有特性

[csharp] view plain copy
  1. public class Example  
  2.    {  
  3.          [DataMemberAttribute]  
  4.          publics string Name  
  5.           {get;set;}  
  6.         ..................  
  7.     }  
  8.     Type type = typeof(Example);          
  9.     PropertyInfo propertyInfo=type.GetProperty("Name");    //獲取Example類的Name屬性  
  10.     foreach (object attribute in propertyInfo.GetCustomAttributes(false))        //遍歷Name屬性的所有特性  
  11.            Console.WriteLine(“Property attribute: "+attribute.ToString());  

總結:

Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的區別
在C#中,我們要使用反射,首先要搞清楚以下命名空間中幾個類的關係:
    System.Reflection命名空間
    (1)   AppDomain:應用程序域,可以將其理解爲一組程序集的邏輯容器
    (2)   Assembly:程序集類
    (3)   Module:模塊類
    (4)   Type:使用反射得到類型信息的最核心的類
    他們之間是一種從屬關係,也就是說,一個AppDomain可以包含N個Assembly,一個Assembly可以包含N個Module,而一個Module可以包含N個Type.

    1,Assembly.Load()
    這個方法通過程序集的長名稱(包括程序集名,版本信息,語言文化,公鑰標記)來加載程序集的,會加載此程序集引用的其他程序集,一般情況下都應該優先使用 這個方法,他的執行效率比LoadFrom要高很多,而且不會造成重複加載的問題(原因在第2點上說明)
    使用這個方法的時候, CLR會應用一定的策略來查找程序集,實際上CLR按如下的順序來定位程序集:
    ⑴如果程序集有強名稱,在首先在全局程序集緩(GAC)中查找程序集。
    ⑵如果程序集的強名稱沒有正確指定或GAC中找不到,那麼通過配置文件中的<codebase>元素指定的URL來查找
    ⑶如果沒有指定強名稱或是在GAC中找不到,CLR會探測特定的文件夾:
    假設你的應用程序目錄是C:\AppDir,<probing>元素中的privatePath指定了一個路徑Path1,你要定位的程序集是AssemblyName.dll則CLR將按照如下順序定位程序集
    C:\AppDir\AssemblyName.dll
    C:\AppDir\AssemblyName\AssemblyName.dll
    C:\AppDir\Path1\AssemblyName.dll
    C:\AppDir\Path1\AssemblyName\AssemblyName.dll
    如果以上方法不能找到程序集,會發生編譯錯誤,如果是動態加載程序集,會在運行時拋出異常!
    2,Assembly.LoadFrom()
    這個方法從指定的路徑來加載程序集,實際上這個方法被調用的時候,CLR會打開這個文件,獲取其中的程序集版本,語言文化,公鑰標記等信息,把他們傳遞給 Load方法,接着,Load方法採用上面的策略來查找程序集。如果找到了程序集,會和LoadFrom方法中指定的路徑做比較,如果路徑相同,該程序集 會被認爲是應用程序的一部分,如果路徑不同或Load方法沒有找到程序集,那該程序集只是被作爲一個"數據文件"來加載,不會被認爲是應用程序的一部分。 這就是在第1點中提到的Load方法比LoadFrom方法的執行效率高的原因。另外,由於可能把程序集作爲"數據文件"來加載,所以使用 LoadFrom從不同路徑加載相同程序集的時候會導致重複加載。當然這個方法會加載此程序集引用的其他程序集。
    3,Assembly.LoadFile()
    這個方法是從指定的文件來加載程序集,和上面方法的不同之處是這個方法不會加載此程序集引用的其他程序集!
    結論:一般大家應該優先選擇Load方法來加載程序集,如果遇到需要使用LoadFrom方法的時候,最好改變設計而用Load方法來代替!
    另:Assembly.LoadFile 與 Assembly.LoadFrom的區別
    1、Assembly.LoadFile只載入相應的dll文件,比如Assembly.LoadFile("abc.dll"),則載入abc.dll,假如abc.dll中引用了def.dll的話,def.dll並不會被載入。
    Assembly.LoadFrom則不一樣,它會載入dll文件及其引用的其他dll,比如上面的例子,def.dll也會被載入。
    2、用Assembly.LoadFrom載入一個Assembly時,會先檢查前面是否已經載入過相同名字的Assembly,比如abc.dll有兩個版本(版本1在目錄1下,版本2放在目錄2下),程序一開始時載入了版本1,當使用Assembly.LoadFrom("2\\abc.dll")載入版本2時,不能載入,而是返回版本1.Assembly.LoadFile的話則不會做這樣的檢查,比如上面的例子換成Assembly.LoadFile的話,則能正確載入版本2.
    LoadFile:加載指定路徑上的程序集文件的內容。LoadFrom: 根據程序集的文件名加載程序集文件的內容。
    區別:
    LoadFile 方法用來來加載和檢查具有相同標識但位於不同路徑中的程序集。但不會加載程序的依賴項。
    LoadFrom 不能用於加載標識相同但路徑不同的程序集。

 

總結:

Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的區別
在C#中,我們要使用反射,首先要搞清楚以下命名空間中幾個類的關係:
    System.Reflection命名空間
    (1)   AppDomain:應用程序域,可以將其理解爲一組程序集的邏輯容器
    (2)   Assembly:程序集類
    (3)   Module:模塊類
    (4)   Type:使用反射得到類型信息的最核心的類
    他們之間是一種從屬關係,也就是說,一個AppDomain可以包含N個Assembly,一個Assembly可以包含N個Module,而一個Module可以包含N個Type.

    1,Assembly.Load()
    這個方法通過程序集的長名稱(包括程序集名,版本信息,語言文化,公鑰標記)來加載程序集的,會加載此程序集引用的其他程序集,一般情況下都應該優先使用 這個方法,他的執行效率比LoadFrom要高很多,而且不會造成重複加載的問題(原因在第2點上說明)
    使用這個方法的時候, CLR會應用一定的策略來查找程序集,實際上CLR按如下的順序來定位程序集:
    ⑴如果程序集有強名稱,在首先在全局程序集緩(GAC)中查找程序集。
    ⑵如果程序集的強名稱沒有正確指定或GAC中找不到,那麼通過配置文件中的<codebase>元素指定的URL來查找
    ⑶如果沒有指定強名稱或是在GAC中找不到,CLR會探測特定的文件夾:
    假設你的應用程序目錄是C:\AppDir,<probing>元素中的privatePath指定了一個路徑Path1,你要定位的程序集是AssemblyName.dll則CLR將按照如下順序定位程序集
    C:\AppDir\AssemblyName.dll
    C:\AppDir\AssemblyName\AssemblyName.dll
    C:\AppDir\Path1\AssemblyName.dll
    C:\AppDir\Path1\AssemblyName\AssemblyName.dll
    如果以上方法不能找到程序集,會發生編譯錯誤,如果是動態加載程序集,會在運行時拋出異常!
    2,Assembly.LoadFrom()
    這個方法從指定的路徑來加載程序集,實際上這個方法被調用的時候,CLR會打開這個文件,獲取其中的程序集版本,語言文化,公鑰標記等信息,把他們傳遞給 Load方法,接着,Load方法採用上面的策略來查找程序集。如果找到了程序集,會和LoadFrom方法中指定的路徑做比較,如果路徑相同,該程序集 會被認爲是應用程序的一部分,如果路徑不同或Load方法沒有找到程序集,那該程序集只是被作爲一個"數據文件"來加載,不會被認爲是應用程序的一部分。 這就是在第1點中提到的Load方法比LoadFrom方法的執行效率高的原因。另外,由於可能把程序集作爲"數據文件"來加載,所以使用 LoadFrom從不同路徑加載相同程序集的時候會導致重複加載。當然這個方法會加載此程序集引用的其他程序集。
    3,Assembly.LoadFile()
    這個方法是從指定的文件來加載程序集,和上面方法的不同之處是這個方法不會加載此程序集引用的其他程序集!
    結論:一般大家應該優先選擇Load方法來加載程序集,如果遇到需要使用LoadFrom方法的時候,最好改變設計而用Load方法來代替!
    另:Assembly.LoadFile 與 Assembly.LoadFrom的區別
    1、Assembly.LoadFile只載入相應的dll文件,比如Assembly.LoadFile("abc.dll"),則載入abc.dll,假如abc.dll中引用了def.dll的話,def.dll並不會被載入。
    Assembly.LoadFrom則不一樣,它會載入dll文件及其引用的其他dll,比如上面的例子,def.dll也會被載入。
    2、用Assembly.LoadFrom載入一個Assembly時,會先檢查前面是否已經載入過相同名字的Assembly,比如abc.dll有兩個版本(版本1在目錄1下,版本2放在目錄2下),程序一開始時載入了版本1,當使用Assembly.LoadFrom("2\\abc.dll")載入版本2時,不能載入,而是返回版本1.Assembly.LoadFile的話則不會做這樣的檢查,比如上面的例子換成Assembly.LoadFile的話,則能正確載入版本2.
    LoadFile:加載指定路徑上的程序集文件的內容。LoadFrom: 根據程序集的文件名加載程序集文件的內容。
    區別:
    LoadFile 方法用來來加載和檢查具有相同標識但位於不同路徑中的程序集。但不會加載程序的依賴項。
    LoadFrom 不能用於加載標識相同但路徑不同的程序集。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章