程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 【C#進階系列】23 程序集加載和反射,

【C#進階系列】23 程序集加載和反射,

編輯:C#入門知識

【C#進階系列】23 程序集加載和反射,


程序集加載

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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved