1. 程式人生 > >C# 反射詳解:定義、建立物件、呼叫例項方法及靜態方法

C# 反射詳解:定義、建立物件、呼叫例項方法及靜態方法

    1、反射的定義及功能介紹:審查元資料並收集關於它的型別資訊的能力。元資料(編譯以後的最基本資料單元)就是一大堆的表,當編譯程式集或者模組時,編譯器會建立一個類定義表,一個欄位定義表,和一個方法定義表等。可能這些說的比較抽象。我再從另一個角度來說:反射是.Net中獲取執行時型別資訊的方式,.Net的應用程式由幾個部分:‘程式集(Assembly)’、‘模組(Module)’、‘型別(class)’組成,而反射提供一種程式設計的方式,讓程式設計師可以在程式執行期獲得這幾個組成部分的相關資訊,例如:Assembly類可以獲得正在執行的裝配件資訊,也可以動態的載入裝配件,以及在裝配件中查詢型別資訊,並建立該型別的例項。
    Type

類可以獲得物件的型別資訊,此資訊包含物件的所有要素:方法、構造器、屬性等等,通過Type類可以得到這些要素的資訊,並且呼叫之。
    MethodInfo包含方法的資訊,通過這個類可以得到方法的名稱、引數、返回值等,並且可以呼叫之。
    諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection名稱空間下。

    System.reflection名稱空間包含的幾個類,允許你反射(解析)這些元資料表的程式碼

    System.Reflection.Assembly:表示一個程式集。

    System.Reflection.Module:在模組上執行反射。

    System.Type:表示各種型別。

    System.Reflection.MethodBase:提供有關方法和建構函式的資訊。

    System.Reflection.MethodInfo:發現方法的屬性並提供對方法元資料的訪問。

    System.Reflection.MemberInfo:獲取或訪問有關成員屬性。

    System.Reflection.FieldInfo:發現欄位屬性並提供對欄位元資料的訪問權。

    System.Reflection.PropertyInfo:發現或訪問屬性(Property)的屬性(Attribute)。

    System.Reflection.EventInfo:發現事件的屬性並提供對事件元資料的訪問權。

    System.Reflection.ConstructorInfo:發現或訪問類建構函式的屬性。

    2、反射層次模型圖如下所示:


    3、獲取程式集元資料

    Assembly類定義了一個程式集,它是一個可重用、無版本衝突並且可自我描述的公共語言執行庫應用程式構造塊。因為程式集中是使用元資料進行自我描述的,所以我們就能通過其元資料得到程式集內部的構成。結合Assembly和反射能夠獲取程式集的元資料,但是首先要將程式集裝入記憶體中。可以使用Assembly類的多種靜態Load方法載入程式集。

    如Assemblyassem =Assembly.Load("University");//載入系統程式集

    4、獲取型別的方法可以通過Type.GetType以及Assembly.GetType方法,如:
             Type  t  =  Type.GetType(“University.College”);
需要注意的是,必須指定它所在的裝配件,University這個動態庫已經引用到這個控制檯程式中了。(全文都是基於我的University.dll及控制檯程式展開講解的)。

    5、動態建立物件以及呼叫方法:在學習如何動態地建立一個物件之前,我們先來學習一下靜態方法和例項方法的區別。

    拿別人一個例子說事:比如說“人”這個類,每個人都有姓名、年齡、性別、身高等,這些屬性就應該是非靜態的,因為每個人都的這些屬性都不相同;但人在生物學上屬於哪個門哪個綱哪個目等,這個屬性是屬於整個人類,所以就應該是靜態的——它不依賴與某個特定的人,不會有某個人是“脊椎動物門哺乳動物綱靈長目”而某個人卻是“偶蹄目”的。

    靜態就是類的,例項就是物件的。靜態方法和例項方法的區別之處還有一個地方:靜態方法不需要依賴類當中的屬性,能在這個方法中封閉的完成一個功能。例項方法更多的會使用到類當中的屬性。

(1)   使用有參函式建立物件。

(2)   通過一個示範例說明:

namespace University
{
    public class College
    {
        private string _code;
       
        private string _address;
        private string _telephone;
        private string _value;
        public string  code
        {
            get
            {
                return _code;
            }
            set
            {
                _code = value;
            }
        }
        
        public string address
        {
            get
            {
                return _address;
            }
            set
            {
                _address = value;
            }
        }
        public string telephone
        {
            get
            {
                return _telephone;
            }
            set
            {
                _telephone = value;
            }
        }
        public string value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }
       
         public College(string  code,string address,string telephone, string value)
        {
            this.code = code;
            this.address = address;
            this.telephone = telephone;
            this.value = value;                   
        }
         public string getCode()
         {
             return code;
         }
        public string getAddress()
        {
            return address;
        }
        public string getTelephone()
        {
            return telephone;
        }
        public string getValue()
        {
            return value;
        }
public void Test(string name)
        {
            Console.WriteLine("向{0}彙報:靜態方法成功呼叫!",name);
        }
    }
}

    //使用有引數建構函式建立物件

 Assembly asm = Assembly.Load("University");
string[] testString = { "01", "第三中學", "西安", "高階" };
object[]parameters = testString;
Object obj =asm.CreateInstance("University.MiddleSchool", true,BindingFlags.Default, null, parameters, null, null);

BindingFlags在前面我們也用到過,它用於限定對型別成員的搜尋。在這裡指定Default,意思是不使用BingdingFlags的策略(你可以把它理解成null,但是BindingFlags是值型別,所以不可能為null,必須有一個預設值,而這個Default就是它的預設值)接下來的引數是Binder,它封裝了CreateInstance繫結物件(Calculator)的規則,我們幾乎永遠都會傳遞null進去,實際上使用的是預定義的DefaultBinder接下來是一個Object[]陣列型別,它包含我們傳遞進去的引數,有引數的建構函式將會使用這些引數;接下來的引數是一個CultureInfo型別,它包含了關於語言和文化的資訊(簡單點理解就是什麼時候ToString)

    //使用InvokeMember呼叫例項方法

           
            Type t = Assembly.Load("University").GetType(string.Format("University.College"));
            string result = (string)t.InvokeMember("getAddress", BindingFlags.InvokeMethod, null, obj, null);
            string result1 = (string)t.InvokeMember("getCode", BindingFlags.InvokeMethod, null, obj, null);
            string result2 = (string)t.InvokeMember("getValue", BindingFlags.InvokeMethod, null, obj, null);
            string result3 = (string)t.InvokeMember("getTelephone", BindingFlags.InvokeMethod, null, obj, null);
            Console.WriteLine(String.Format("Theresult is {0},{1},{2},{3}", result, result1, result2, result3));



    結果為:The result is 第三中學,01,高階,西安

       在InvokeMember方法中,第一個引數說明了想要呼叫的方法名稱;第二個引數說明是呼叫方法(因為InvokeMember的功能非常強大,不光是可以呼叫方法,還可以獲取/設定 屬性、欄位等。此列舉的詳情可參看MSDN);第三個引數是Binder,null說明使用預設的Binder;第四個引數說明是在這個物件上(obj是College型別的例項)進行呼叫;最後一個引數是陣列型別,表示的是方法所接受的引數。

    //使用InvokeMember對靜態方法的呼叫 

            Type t1 = Assembly.Load("University").GetType(string.Format("University.College"));
            Object[] parameters2 = {"John"};          
            t1.InvokeMember("Test", BindingFlags.InvokeMethod,null,obj, parameters2);

    結果為:向John彙報:靜態方法成功呼叫!

    我們和上面對比一下:首先,第四個引數傳遞的是 obj,不再是一個Calculator例項型別,這很容易理解,因為我們呼叫的是一個靜態方法,它不是基於某個具體的型別例項的,而是基於型別本身;其次,因為我們的靜態方法需要提供一個引數,所以我們以陣列的形式將這個引數進行了傳遞。