程序集與反射技術(C#)


       首先我們來看程序集,程序集是代碼進行編譯是的一個邏輯單元,把相關的代碼和類型進行組合,然后生成PE文件(例如可執行文件.exe和類庫文件.dll)。由於程序集在編譯后並不一定會生成單個文件,而可能會生成多個物理文件,甚至可能會生成分布在不同位置的多個物理文件,所以程序集是一個邏輯單元,而不是一個物理單元。即程序集在邏輯上是一個編譯單元,但在物理儲存上可以有多種存在形式。對於靜態程序集可以生成單個或多個文件,而動態程序集是存在於內存中的。在C#中程序集處處可見,因為任何基於.NET的代碼在編譯時都至少存在一個程序集(所有.NET項目都會默認引用mscorlib程序集)。

        基於.NET框架的.dll庫是一個完整的程序集,需要事先引用對應的類庫。從代碼的結構上看,一個程序集可以包含一個或多個命名空間,而每個命名空間中又可以包含子命名空間或類型列表。由於程序集在編譯后可以生成多個模塊文件,因此一個物理文件並不代表它就是一個程序集,一個程序集並不一定只有一個文件。不過,在VS開發環境中,一個解決方案可以包含多個項目,而每個項目就是一個程序集。

      程序集信息其實是通過在程序集上應用各種Attribute來設置的,並且這些特性都位於System.Reflection命名空間,也就是這一系列Attribute提供給反射技術,可用於獲取程序集的基本信息.

       應用程序結構分為應用程序域—程序集—模塊—類型—成員幾個層次,公共語言運行庫加載器管理應用程序域,這種管理包括將每個程序集加載到相應的應用程序域以及控制每個程序集中類型層次結構的內存布局。 
        程序集包含模塊,而模塊包含類型,類型又包含成員,反射則提供了封裝程序集、模塊和類型的對象。我們可以使用反射動態地創建類型的實例,將類型綁定到現有對象或從現有對象中獲取類型,然后調用類型的方法或訪問其字段和屬性。反射通常具有以下用途。 
(1)使用Assembly定義和加載程序集,加載在程序集清單中列出模塊,以及從此程序集中查找類型並創建該類型的實例。 
(2)使用Module了解包含模塊的程序集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。 
(3)使用ConstructorInfo了解構造函數的名稱、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法來調用特定的構造函數。 
(4)使用MethodInfo了解方法的名稱、返回類型、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法來調用特定的方法。 
(5)使用FiedInfo了解字段的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,並獲取或設置字段值。 
(6)使用EventInfo了解事件的名稱、事件處理程序數據類型、自定義屬性、聲明類型和反射類型等,添加或移除事件處理程序。 
(7)使用PropertyInfo了解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,獲取或設置屬性值。

(8)使用ParameterInfo了解參數的名稱、數據類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。 

 

動態加載程序集

1)通過程序集的長名稱加載,一般用於加載位於全局程序集緩存中的程序集。(.NET框架的類庫都位於全局程序集緩存中)(使用GacUtil命令行工具可以獲取System程序集的長名稱);

(2)通過指定的文件加載程序集,通常是.dll或.exe文件。

例如:本人的計算機上的全局程序集緩存包含下列程序集:

system, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL
system, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL

項目數 = 2

 

      反射(Reflection)是.NET中的重要機制,通過反射,可以在運行時獲得.NET中每一個類型(包括類、結構、委托、接口和枚舉等)的成員,包括方法、屬性、事件,以及構造函數等。還可以獲得每個成員的名稱、限定符和參數等。有了反射,即可對每一個類型了如指掌。如果獲得了構造函數的信息,即可直接創建對象,即使這個對象的類型在編譯時還不知道。

1)反射用於獲取類型信息。下面一個例子:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Reflection;
 7 
 8 namespace My
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             // 加載程序集
15             Assembly ass = Assembly.Load("system.windows.forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
16             // 獲取Button類的Type
17             Type typeofButton = ass.GetType("System.Windows.Forms.Button", false);
18             if (typeofButton != null)
19             {
20                 // 獲得Button類型的公共屬性列表
21                 PropertyInfo[] props = typeofButton.GetProperties();
22                 // 輸出屬性信息
23                 Console.WriteLine("\n  Button類的公共屬性列表:");
24                 foreach (PropertyInfo p in props)
25                 {
26                     Console.WriteLine("  屬性名:{0},屬性類型:{1}", p.Name, p.PropertyType.Name);
27                 }
28                 // 獲取Button類型的公共事件列表
29                 EventInfo[] events = typeofButton.GetEvents();
30                 // 輸出事件信息
31                 Console.WriteLine("\n  Button類的公共事件列表:");
32                 foreach (EventInfo ev in events)
33                 {
34                     Console.WriteLine("  事件名:{0},事件類型:{1}", ev.Name, ev.EventHandlerType.Name);
35                 }
36             }
37             Console.Read();
38        }
39     }
40 }

2)反射還可以在運行階段動態創建類型實例,然后就可以動態調用實例的成員。

動態創建實例有兩種方法可以選擇:

第一種:使用Activator類的CreateInstance靜態方法,其中方法返回的對象就是對應的實例引用。

看個例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace Me
{
    class Program
    {
        static void Main(string[] args)
        {
            // 從當前執行的程序集中獲取Test類型的Type
            Assembly ass = Assembly.GetExecutingAssembly();
            Type tp = ass.GetType("Me.Test", false);
            if (tp != null)
            {
                // 創建實例
                object instance = Activator.CreateInstance(tp, 105);
                if (instance != null)
                {
                    // 獲取實例的公共屬性
                    PropertyInfo[] props = tp.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    // 顯示屬性信息
                    foreach (var p in props)
                    {
                        Console.WriteLine("{0} : {1}", p.Name, p.GetValue(instance));
                    }
                }
            }

            Console.Read();
        }
    }
    public class Test
    {
        public int NumValue { get; private set; }

        public Test(int sead)
        {
            Random rand = new Random(sead);
            NumValue = rand.Next();
        }
    }
}

第二種:通過反射找出類型的構造函數,然后調用構造函數來獲取實例引用。

代碼如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Reflection;
 7 
 8 namespace My
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             // 從mscorlib程序集中獲取DateTime結構的Type
15             Type tpdatetime = Type.GetType("System.DateTime", false);
16             if (tpdatetime != null)
17             {
18                 // 獲取具有六個參數的構造函數
19                 ConstructorInfo constructor = tpdatetime.GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) });
20                 if (constructor != null)
21                 {
22                     // 調用構造函數創建實例
23                     object instance = constructor.Invoke(new object[] { 2015, 9, 27, 13, 25, 27 });
24                     // 獲取實例的屬性列表
25                     if (instance != null)
26                     {
27                         PropertyInfo[] props = tpdatetime.GetProperties(BindingFlags.Public | BindingFlags.Instance);
28                         Console.WriteLine("以下是DateTime實例的屬性值列表:");
29                         foreach (PropertyInfo p in props)
30                         {
31                             // 獲取屬性值
32                             object objval = p.GetValue(instance);
33                             // 顯示屬性名稱與屬性值
34                             Console.WriteLine("{0,-15} :{1}", p.Name, objval ?? string.Empty);
35                         }
36                     }
37                 }
38             }
39 
40             Console.Read();
41         }
42     }
43 }

學習到這里,相信你已經對這部分知識有啦相當一部分了解,由於是第一次寫博客,或許寫的不是很好,若有錯,請聯系我,下期再見。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM