程序集加載
程序集加載,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方法那麼就會和一般的寫程序一樣簡單了。