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