程式集載入 程式集載入,CLR使用System.Reflection.Assembly.Load靜態方法,當然這個方法我們自己也可以顯式調用。 還有一個Assembly.LoadFrom方法載入指定路徑名的程式集,實際上其內部是先通過AssemblyName.GetAssemblyName獲取Ass ...
程式集載入
程式集載入,CLR使用System.Reflection.Assembly.Load靜態方法,當然這個方法我們自己也可以顯式調用。
還有一個Assembly.LoadFrom方法載入指定路徑名的程式集,實際上其內部是先通過AssemblyName.GetAssemblyName獲取AssemblyName對象,然後調用Assembly.Load方法。
此時load方法會在各個位置(前面03章講過)查找程式集,如果已經載入了此程式集就返回已載入的程式集,如果沒有載入就去載入找到的程式集,如果沒有找到,就載入路徑所給的那個程式集。(所以很清楚瞭解到不一定會載入所指定的那個程式集,而可能是另一個。在這裡如果每次生成強命名程式集時更新版本號,才會使LoadFrom方法的行為符合預期)
LoadFrom方法允許傳遞一個Url作為實參,CLR會下載文件,把它安裝到用戶的下載緩存中,再從那兒載入文件。
ReflectionOnlyLoadFrom函數也可以載入程式集,且禁止程式集中的任何代碼執行。
使用反射構建動態可擴展應用程式
既然載入了程式集,那麼就應該要有辦法去使用程式集中定義的類,這種辦法就是反射。
利用System.Reflection命名空間中包含的類型,可以寫代碼來反射元數據表,為所載入的程式集中所包含的元數據提供對象模型。
反射一些例子:
首先先建立一個用於反射的程式集,代碼如下:
namespace HelloWorld { public class Man { public string _name; public Man(String name) { this._name = name; } public void ShowName() { Console.WriteLine(this._name); } } } namespace HelloWorld { public class Troy:Man { private string _jobName; public Troy(string name,string jobName):base(name) { this._jobName = jobName; } public void ShowJobName() { Console.WriteLine(this._jobName); } } }
然後生成了一個叫HelloWorld.dll的文件,然後開始玩反射
//首先載入程式集,獲取程式集對象 Assembly myAssembly=Assembly.LoadFrom("D:\\HelloWorld.dll"); //玩程式集中定義的公共類型 foreach (Type type in myAssembly.ExportedTypes) { //列印類型全名 Console.WriteLine("類型全名:"+type.FullName); Console.WriteLine(type.FullName + "的基類:" + type.BaseType.FullName); //判定類型是否為String(當然這是不可能的,因為只有Man和Troy) if (type == typeof(String)) { Console.WriteLine("有個String類型"); } //Type對象是輕量型的類型引用,更全面的信息在TypeInfo對象(獲取TypeInfo對象會強迫CLR確保已載入類型的定義程式集,從而對類型進行解析。(代價高昂)), //如下轉換 TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(type); //也可以反著轉 Type tmpType = typeInfo.AsType(); //泛型類型的Type Type openType = typeof(Dictionary<,>);//開放類型 Type closedType= openType.MakeGenericType(typeof(int), type);//閉合類型 //實例化 Object obj= Activator.CreateInstance(closedType); Console.WriteLine(obj.GetType()); }
反射的性能
反射是相當強大的機制,但是也有其缺點:
- 反射造成編譯時無法保證類型安全性,因為它是在運行時才依靠字元串來對類進行實例化等操作。
- 反射的速度很慢,因為是在運行時靠字元串去標識成員,發現它們,使用它們。整個過程中都是用字元串來搜索。
設計支持載入項的應用程式
構建可擴展應用程式時,一般使用介面而不是基類,因為介面允許載入項開發人員選擇自己的基類。
為宿主介面類的方法定義參數和返回類時,嘗試使用MSCorLib.dll定義的介面和類型。因為CLR只載入一個MSCorLib.dl,所以不會出現類型版本不匹配的情況,且有助於減少應用程式對記憶體的需求。
反射與類型的成員
System.Reflection.MemberInfo封裝了所有類型成員都通用的一組屬性。它的一些派生類如MethodInfo則封裝了與特定類型成員相關的更多屬性。
直接上代碼簡單易懂:
class Program { static void Main(string[] args) { Type type = typeof(Troy); Object obj = Activator.CreateInstance(type); MethodInfo[] arrMethod= type.GetMethods(); foreach (var methodInfo in arrMethod) { if (methodInfo.GetParameters().Length == 0) { methodInfo.Invoke(obj, null); } } Console.Read(); } } public class Troy{ public string name; public Troy() { name = "Troy"; } public void Show() { Console.WriteLine(name); } }
對於FieldInfo(欄位)和PropertyInfo(屬性)可以用GetValue和SetValue來獲取和設置實例的值,
對於MethodInfo(方法)和ConstructorInfo(構造器)則可以用Invoke來調用,
對於EventInfo(事件)可以用AddEventHandler和RemoveHandler來增加事件回調函數和減少回調函數。
上述方法其實很麻煩,如果用dynamic方法那麼就會和一般的寫程式一樣簡單了。