1. 程式人生 > >.Net 中的反射(檢視基本型別資訊)

.Net 中的反射(檢視基本型別資訊)

反射概述 和Type類

1.反射的作用

簡單來說,反射提供這樣幾個能力:1、檢視和遍歷型別(及其成員)的基本資訊和程式集元資料(metadata);2、遲繫結(Late-Binding)方法和屬性。3、動態建立型別例項(並可以動態呼叫所建立的例項的方法、欄位、屬性)。序章中,我們所採用的那個例子,只是反射的一個用途:檢視型別成員資訊。接下來的幾個章節,我們將依次介紹反射所提供的其他能力。

2.獲取Type物件例項

反射的核心是Type類,這個類封裝了關於物件的資訊,也是進行反射的入口。當你獲得了關於型別的Type物件後,就可以根據Type提供的屬性和方法獲取這個型別的一切資訊(方法、欄位、屬性、事件、引數、建構函式等)。我們開始的第一步,就是獲取關於型別的Type例項。獲取Type物件有兩種形式,一種是獲取當前載入程式集中的型別(Runtime),一種是獲取沒有載入的程式集的型別。

我們先考慮Runtime時的Type,一般來說有三種獲取方法:

2.1使用Type類提供的靜態方法GetType()

比如我們想要獲得Stream型別的Type例項,則可以這樣:

Type t = Type.GetType("System.IO.Stream");
txtOutput.Text = t.ToString();

注意到GetType方法接受字串形式的型別名稱。

2.2 使用 typeof 操作符

也可以使用C# 提供的typeof 操作符來完成這一過程:

// 如果在頁首寫入了using System.IO; 也可以直接用 typeof(Stream);
Type t = typeof

(System.IO.Stream);

這時的使用有點像泛型,Stream就好像一個型別引數一樣,傳遞到typeof操作符中。

2.3 通過型別例項獲得Type物件

我們還可以通過型別的例項來獲得:

String name ="Jimmy Zhang";
Type t = name.GetType();

使用這種方法時應當注意,儘管我們是通過變數(例項)去獲取Type物件,但是Type物件不包含關於這個特定物件的資訊,仍是儲存物件的型別(String)的資訊。

3.Type型別 及 Reflection名稱空間的組織結構

到現在為止,我已經多次提過Type封裝了型別的資訊,那麼這些型別資訊都包含什麼內容呢?假設我們現在有一個型別的例項,它的名字叫做 demo,我們對它的資訊一無所知,並通過下面程式碼獲取了對於它的Type例項:

// 前面某處的程式碼例項化了demo物件
Type t = demo.GetType();

現在,我們期望 t 包含了關於 demo 的哪些資訊呢?

3.1 demo的型別的基本資訊

  • 我們當然首先想知道 demo 是什麼型別的,也就是 demo 的型別名稱。
  • 我們還想知道該型別位於什麼名稱空間下。
  • 它的基型別是什麼,以及它在.Net執行庫中的對映型別。
  • 它是值型別還是引用型別。
  • 它是不是Public的。
  • 它是列舉、是類、是陣列、還是介面。
  • 它是不是基礎型別(int等)。
  • 等等 ...

Type 提供了下面的屬性,用於獲取型別的基本資訊,常用的有下面一些:

屬 性 說 明
Name 獲取型別名稱
FullName 型別全名
Namespace 名稱空間名稱
BaseType 獲取對於基類的Type型別的引用
UnderlyingSystemType 在.Net中對映的型別的引用
Attributes 獲取TypeAttributes位標記
IsValueType 是否值型別
IsByRef 是否由引用傳遞
IsEnum 是否列舉
IsClass 是否類
IsInterface 是否介面
IsSealed 是否密封類
IsPrimitive 是否基型別(比如int)
IsAbstract 是否抽象
IsPublic 是否公開
IsNotPublic 是否非公開
IsVisible 是否程式集可見
等等...

3.2 demon的型別的成員資訊  

  • 我們可能還想知道它有哪些欄位。
  • 有些什麼屬性,以及關於這些屬性的資訊。
  • 有哪些建構函式。
  • 有哪些方法,方法有哪些引數,有什麼樣的返回值。
  • 包含哪些事件。
  • 實現了哪些介面。
  • 我們還可以不加區分地獲得它的所有 以上成員。

觀察上面的列表,就拿第一條來說,我們想獲取型別都有哪些欄位,以及這些欄位的資訊。而欄位都包含哪些資訊呢?可能有欄位的型別、欄位的名稱、欄位是否public、欄位是否為const、欄位是否是read only 等等,那麼是不是應該將欄位的這些資訊也封裝起來呢?

實際上,.Net中提供了 FiledInfo 型別,它封裝了關於欄位的相關資訊。對照上面的列表,類似的還有 PropertyInfo型別、ConstructorInfo型別、MethodInfo型別、EventInfo型別。而對於方法而言,對於它的引數,也會有in引數,out引數,引數型別等資訊,類似的,在 System.Reflection 名稱空間下,除了有上面的提到的那麼多Info字尾結尾的型別,還有個ParameterInfo 型別,用於封裝方法的引數資訊。

最後,應該注意到 Type 型別,以及所有的Info型別均 繼承自 MemberInfo 型別,MemberInfo型別提供了獲取型別基礎資訊的能力。

在VS2005中鍵入Type,選中它,再按下F12跳轉到Type型別的定義,縱覽Type型別的成員,發現可以大致將屬性和方法分成這樣幾組:

  • IsXXXX,比如 IsAbstract,這組bool屬性用於說明型別的某個資訊。(前面的表格已經列舉了一些。)
  • GetXXXX(),比如GetField(),返回FieldInfo,這組方法用於獲取某個成員的資訊。
  • GetXXXXs(),比如GetFields(),返回FieldInfo[],這組方法使用者獲取某些成員資訊。
  • 還有其他的一些屬性和方法,等後面遇到了再說。

由於MemberInfo是一個基類,當我們獲得一個MemberInfo後,我們並不知道它是PropertyInfo(封裝了屬性資訊的物件)還是FieldInfo(封裝了屬性資訊的物件),所以,有必要提供一個辦法可以讓我們加以判斷,在Reflection 名稱空間中,會遇到很多的位標記,這裡先介紹第一個位標記(本文管用[Flags]特性標記的列舉稱為 位標記),MemberTypes,它用於標記成員型別,可能的取值如下:

[Flags]
public enumMemberTypes {
    Constructor = 1,  //  該成員是一個建構函式
    Event = 2,        //  該成員是一個事件
    Field = 4,        //  該成員是一個欄位
    Method = 8,           //  該成員是一個方法
    Property = 16,    //  該成員是一個屬性
    TypeInfo = 32,    //  該成員是一種型別
    Custom = 64,      //  自定義成員型別
    NestedType = 128, //  該成員是一個巢狀型別
    All = 191,        //  指定所有成員型別。
}

反射程式集

在.Net中,程式集是進行部署、版本控制的基本單位,它包含了相關的模組和型別,我並不打算詳細地去說明程式集及其構成,只是講述如何通過反射獲取程式集資訊。

在System.Reflection名稱空間下有一個Assembly型別,它代表了一個程式集,幷包含了關於程式集的資訊。

在程式中載入程式集時,一般有這麼幾個方法,我們可以使用 Assembly型別提供的靜態方法LoadFrom() 和 Load(),比如:

Assembly asm = Assembly.LoadFrom("Demo.dll");

或者

Assembly asm = Assembly.Load("Demo");

當使用LoadFrom()方法的時候,提供的是程式集的檔名,當將一個程式集新增到專案引用中以後,可以直接寫“檔名.dll”。如果想載入一個不屬於當前專案的程式集,則需要給出全路徑,比如:

Assembly asm = Assembly.LoadFrom(@"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Web.dll");

使用Load()方法的時候,只用提供程式集名稱即可,不需要提供程式集的字尾名。如果想獲得當前程式集,可以使用Assembly型別的靜態方法 GetExecutingAssembly,它返回包含當前執行的程式碼的程式集(也就是當前程式集)。

Assembly as = Assembly.GetExecutingAssembly();

在獲得一個Type型別例項以後,我們還可以使用該例項的Assembly屬性來獲得其所在的程式集:

Type t = typeof(int)
Assembly asm = t.Assembly;

一個程式集可能有多個模組(Module)組成,每個模組又可能包含很多的型別,但.Net的預設編譯模式一個程式集只會包含一個模組,我們現在看下 反射 提供了什麼樣的能力讓我們獲取關於程式集的資訊(只列出了部分常用的):

屬 性/方 法 說 明
FullName 程式集名稱
Location 程式集的路徑
GetTypes() 獲取程式集包含的全部型別
GetType() 獲取某個型別
GetModules() 獲取程式集包含的模組
GetModule() 獲取某個模組
GetCustomAttributes() 獲取自定義特性資訊

NOTE:程式集和名稱空間不存在必然聯絡,一個程式集可以包含多個名稱空間,同一個名稱空間也可以分放在幾個程式集。

為了方便進行我們後面的測試,我們現在建立一個Windows控制檯應用程式,我給它起名叫SimpleExplore;然後再新增一個Demo類庫專案,我們將來編寫的程式碼就使用者檢視這個Demo專案集的型別資訊 或者 是對這個程式集中的型別進行遲繫結。這個Demon專案只包含一個名稱空間Demo,為了體現儘可能多的型別同時又Keep Simple,其程式碼如下:

namespace Demo {

    public abstractclass BaseClass {
      
    }

    public structDemoStruct { }

    public delegatevoid DemoDelegate(Object sender, EventArgs e);

    public enumDemoEnum {
       terrible, bad, common=4, good, wonderful=8
    }

    public interfaceIDemoInterface {
       void SayGreeting(string name);     
    }

    public interfaceIDemoInterface2 {}
   
    public sealedclass DemoClass:BaseClass, IDemoInterface,IDemoInterface2 {

       private string name;
       public string city;
       public readonlystring title;
       public conststring text ="Const Field";
       public eventDemoDelegate myEvent;     
             
       public string Name {
           private get { return name; }
           set { name = value; }
       }

       public DemoClass() {
           title = "Readonly Field";
       }

       public classNestedClass { }

       public void SayGreeting(string name) {
           Console.WriteLine("Morning :" + name);
       }
    }

}

現在我們在 SimpleExplore專案中寫一個方法AssemblyExplor(),檢視我們Demo專案生成的程式集Demo.dll定義的全部型別:

public static void AssemblyExplore() {
    StringBuilder sb = new StringBuilder();

    Assembly asm = Assembly.Load("Demo");

    sb.Append("FullName(全名):" + asm.FullName + "\n");
    sb.Append("Location(路徑):" + asm.Location + "\n");

    Type[] types = asm.GetTypes();

    foreach (Type t in types) {
       sb.Append("   型別:" + t + "\n");
    }

    Console.WriteLine(sb.ToString());
}

然後,我們在Main()方法中呼叫一下,應該可以看到這樣的輸出結果:

FullName(全名):Demo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Location(路徑):E:\MyApp\TypeExplorer\SimpleExplorer\bin\Debug\Demo.dll
模組: Demo.dll
   型別:Demo.BaseClass
   型別:Demo.DemoStruct
   型別:Demo.DemoDelegate
   型別:Demo.DemoEnum
   型別:Demo.IDemoInterface
   型別:Demo.IDemoInterface2
   型別:Demo.DemoClass
   型別:Demo.DemoClass+NestedClass

反射基本型別

這裡說反射基本型別,基本型別是針對 泛型型別 來說的,因為 反射泛型 會更加複雜一些。在前面的範例中,我們獲得了程式集中的所有型別,並迴圈列印了它們,列印結果僅僅顯示出了型別的全名,而我們通常需要關於型別更詳細的資訊,本節我們就來看看如何進一步檢視型別資訊。

NOTE:因為一個程式集包含很多型別,一個型別包含很多成員(方法、屬性等),一個成員又包含很多其他的資訊,所以如果我們從程式集層次開始寫程式碼去獲取每個層級的資訊,那麼會巢狀很多的foreach語句,為了閱讀方便,我會去掉最外層的迴圈。

1.獲取基本資訊

有了前面Type一節的介紹,我想完成這裡應該只是打打字而已,所以我直接寫出程式碼,如有必要,會在註釋中加以說明。我們再寫一個方法TypeExplore,用於獲取型別的詳細資訊(記得AssemblyExplore只獲取了型別的名稱):

public static void TypeExplore(Type t) {
    StringBuilder sb = new StringBuilder();

    sb.Append("名稱資訊:\n");
    sb.Append("Name: " + t.Name + "\n");
    sb.Append("FullName: " + t.FullName + "\n");
    sb.Append("Namespace: " + t.Namespace + "\n");

    sb.Append("\n其他資訊:\n");
    sb.Append("BaseType(基型別): " + t.BaseType + "\n");
    sb.Append("UnderlyingSystemType: " + t.UnderlyingSystemType + "\n");

    sb.Append("\n型別資訊:\n");
    sb.Append("Attributes(TypeAttributes位標記): " + t.Attributes + "\n");
    sb.Append("IsValueType(值型別): " + t.IsValueType + "\n");
    sb.Append("IsEnum(列舉): " + t.IsEnum + "\n");
    sb.Append("IsClass(類): " + t.IsClass + "\n");
    sb.Append("IsArray(陣列): " + t.IsArray + "\n");
    sb.Append("IsInterface(介面): " + t.IsInterface + "\n");
    sb.Append("IsPointer(指標): " + t.IsPointer + "\n");
    sb.Append("IsSealed(密封): " + t.IsSealed + "\n");
    sb.Append("IsPrimitive(基型別): " + t.IsPrimitive + "\n");
    sb.Append("IsAbstract(抽象): " + t.IsAbstract + "\n");
    sb.Append("IsPublic(公開): " + t.IsPublic + "\n");
    sb.Append("IsNotPublic(不公開): " + t.IsNotPublic + "\n");
    sb.Append("IsVisible: " + t.IsVisible + "\n");
    sb.Append("IsByRef(由引用傳遞): " + t.IsByRef + "\n");

    Console.WriteLine(sb.ToString());
}

然後,我們在Main方法中輸入:

Type t = typeof(DemoClass);
TypeExplore(t);

會得到這樣的輸出:

名稱資訊:
Name: DemoClass
FullName: Demo.DemoClass
Namespace: Demo

其他資訊:
BaseType(基型別): Demo.BaseClass
UnderlyingSystemType: Demo.DemoClass

型別資訊:
Attributes(TypeAttributes位標記): AutoLayout, AnsiClass, Class, Public, Sealed,
BeforeFieldInit
IsValueType(值型別): False
IsEnum(列舉): False
IsClass(類): True
IsArray(陣列): False
IsInterface(介面): False
IsPointer(指標): False
IsSealed(密封): True
IsPrimitive(基型別): False
IsAbstract(抽象): False
IsPublic(公開): True
IsNotPublic(不公開): False
IsVisible: True
IsByRef(由引用傳遞): False

值得注意的是Attributes屬性,它返回一個TypeAttributes位標記,這個標記標識了型別的一些元資訊,可以看到我們熟悉的Class、Public、Sealed。相應的,IsClass、IsSealed、IsPublic等屬性也返回為True。

2.成員資訊 與 MemberInfo 型別

我們先考慮一下對於一個型別Type,可能會包含什麼型別,常見的有欄位、屬性、方法、建構函式、介面、巢狀型別等。MemberInfo 類代表著 Type的成員型別,值得注意的是Type類本身又繼承自MemberInfo類,理解起來並不困難,因為一個型別經常也是另一型別的成員。Type類提供 GetMembers()、GetMember()、FindMember()等方法用於獲取某個成員型別。

我們再新增一個方法 MemberExplore(),來檢視一個型別的所有成員型別。

public static void MemberExplore(Type t) {
    StringBuilder sb = new StringBuilder();

    MemberInfo[] memberInfo = t.GetMembers();

    sb.Append("檢視型別 " + t.Name + "的成員資訊:\n");

    foreach (MemberInfo mi in memberInfo) {
       sb.Append("成員:" + mi.ToString().PadRight(40) + " 型別: " + mi.MemberType + "\n");
    }

    Console.WriteLine(sb.ToString());
}

然後我們在Main方法中呼叫一下。

MemberExplore(typeof(DemoClass));

產生的輸出如下:

檢視型別 DemoClass的成員資訊:
--------------------------------------------------
成員:Void add_myEvent(Demo.DemoDelegate)      型別: Method
成員:Void remove_myEvent(Demo.DemoDelegate)   型別: Method
成員:System.String get_Name()                 型別: Method
成員:Void set_Name(System.String)             型別: Method
成員:Void SayGreeting(System.String)          型別: Method
成員:System.Type GetType()                    型別: Method
成員:System.String ToString()                 型別: Method
成員:Boolean Equals(System.Object)            型別: Method
成員:Int32 GetHashCode()                      型別: Method
成員:Void .ctor()                             型別: Constructor
成員:System.String Name                       型別: Property
成員:Demo.DemoDelegate myEvent                型別: Event
成員:System.String text                       型別: Field
成員:Demo.DemoClass+NestedClass               型別: NestedType

我們使用了GetMembers()方法獲取了成員資訊的一個數組,然後遍歷了陣列,列印了成員的名稱和型別。如同我們所知道的:Name屬性在編譯後成為了get_Name()和set_Name()兩個獨立的方法;myEvent事件的註冊(+=)和取消註冊(-=)分別成為了add_myEvent()和remove_myEvent方法。同時,我們發現私有(private)欄位name 沒有被打印出來,另外,基類System.Object的成員GetType()和Equals()也被列印了出來。

有的時候,我們可能不希望檢視基類的成員,也可能希望檢視私有的成員,此時可以使用GetMembers()的過載方法,傳入BindingFlags 位標記引數來完成。BindingFlags位標記對如何獲取成員的方式進行控制(也可以控制如何建立物件例項,後面會說明)。對於本例,如果我們想獲取所有的公有、私有、靜態、例項 成員,那麼只需要這樣修改GetMembers()方法就可以了。

MemberInfo[] memberInfo = t.GetMembers(
    BindingFlags.Public |
    BindingFlags.Static |
    BindingFlags.NonPublic |
    BindingFlags.Instance |
    BindingFlags.DeclaredOnly
);

此時的輸出如下:

檢視型別 DemoClass的成員資訊:
--------------------------------------------------
成員:Void add_myEvent(Demo.DemoDelegate)      型別: Method
成員:Void remove_myEvent(Demo.DemoDelegate)   型別: Method
成員:System.String get_Name()                 型別: Method
成員:Void set_Name(System.String)             型別: Method
成員:Void SayGreeting(System.String)          型別: Method
成員:Void .ctor()                             型別: Constructor
成員:System.String Name                       型別: Property
成員:Demo.DemoDelegate myEvent                型別: Event
成員:System.String name                       型別: Field
成員:Demo.DemoDelegate myEvent                型別: Field
成員:System.String text                       型別: Field
成員:Demo.DemoClass+NestedClass               型別: NestedType

可以看到,繼承自基類 System.Object 的方法都被過濾掉了,同時,打印出了私有的 name, myEvent 等欄位。

現在如果我們想要獲取所有的方法(Method),那麼我們可以使用 Type類的FindMembers()方法:

MemberInfo[] memberInfo = t.FindMembers(
    MemberTypes.Method,      // 說明查詢的成員型別為 Method
    BindingFlags.Public |
    BindingFlags.Static |
    BindingFlags.NonPublic |
    BindingFlags.Instance |
    BindingFlags.DeclaredOnly,
    Type.FilterName,
    "*"
);

Type.FilterName 返回一個MemberFilter型別的委託,它說明按照方法名稱進行過濾,最後一個引數“*”,說明返回所有名稱(如果使用“Get*”,則會返回所有以Get開頭的方法)。現在的輸出如下:

檢視型別 DemoClass的成員資訊:
--------------------------------------------------
成員:Void add_myEvent(Demo.DemoDelegate)      型別: Method
成員:Void remove_myEvent(Demo.DemoDelegate)   型別: Method
成員:System.String get_Name()                 型別: Method
成員:Void set_Name(System.String)             型別: Method
成員:Void SayGreeting(System.String)          型別: Method

MemberInfo 類有兩個屬性值得注意,一個是DeclaringType,一個是 ReflectedType,返回的都是Type型別。DeclaredType 返回的是宣告該成員的型別。比如說,回顧我們之前的一段程式碼:

MemberInfo[] members = typeof(DemoClass).GetMembers();

它將返回所有的公有成員,包括繼承自基類的Equals()等方法,對於Equals()方法來說,它的 DeclaringType 返回的是相當於 typeof(Object) 的型別例項,因為它是在 System.Object中被定義的;而它的ReflectedType 返回的則是相當於 typeof(DemoClass) 型別例項,因為它是通過 DemoClass 的型別例項被獲取的。

3.欄位資訊 與 FieldInfo型別

如同我們之前所說,MemberInfo 是一個基類,它包含的是型別的各種成員都公有的一組資訊。實際上,對於欄位、屬性、方法、事件 等型別成員來說,它們包含的資訊顯然都是不一樣的,所以,.Net 中提供了 FiledInfo 型別來封裝欄位的資訊,它繼承自MemberInfo。

如果我們希望獲取一個型別的所有欄位,可以使用 GetFileds()方法。我們再次新增一個方法FieldExplore():

public static void FieldExplore(Type t) {
    StringBuilder sb = new StringBuilder();
   
    FieldInfo[] fields = t.GetFields();

    sb.Append("檢視型別 " + t.Name + "的欄位資訊:\n");
    sb.Append(String.Empty.PadLeft(50, '-') + "\n");

    foreach (FieldInfo fi in fields) {
       sb.Append("名稱:" + fi.Name + "\n");
       sb.Append("型別:" + fi.FieldType + "\n");
       sb.Append("屬性:" + fi.Attributes + "\n\n");
    }

    Console.WriteLine(sb.ToString());
}

產生的輸出如下:

檢視型別 DemoClass的欄位資訊:
--------------------------------------------------
名稱:city
型別:System.String
屬性:Public

名稱:title
型別:System.String
屬性:Public, InitOnly

名稱:text
型別:System.String
屬性:Public, Static, Literal, HasDefault

值得一提的是fi.FieldType 屬性,它返回一個FieldAttributes位標記,這個位標記包含了欄位的屬性資訊。對比我們之前定義的DemoClass類,可以看到,對於title 欄位,它的屬性是public, InitOnly;對於Const型別的text欄位,它的屬性為Public,Static,Literal,HasDefault,由此也可以看出,宣告一個const型別的變數,它預設就是靜態static的,同時,由於我們給了它初始值,所以位標記中也包括HasDefault。

針對於FieldType位標記,FiledInfo 類提供了一組返回為bool型別的屬性,來說明欄位的資訊,常用的有:IsPublic, IsStatic, IsInitOnly, IsLiteral, IsPrivate 等。

如果我們想要獲取私有欄位資訊,依然可以使用過載了的GetFields[]方法,傳入BindingFlags引數,和上面的類似,這裡就不重複了。

4.屬性資訊 與 PropertyInfo 型別

和欄位類似,也可以通過 GetProperty()方法,獲取型別的所有屬性資訊。

public static void PropertyExplore(Type t) {
    StringBuilder sb = new StringBuilder();
    sb.Append("檢視型別 " + t.Name + "的屬性資訊:\n");
    sb.Append(String.Empty.PadLeft(50, '-') + "\n");

    PropertyInfo[] properties = t.GetProperties();

    foreach (PropertyInfo pi in properties) {
       sb.Append("名稱:" + pi.Name + "\n");
       sb.Append("型別:" + pi.PropertyType + "\n");
       sb.Append("可讀:" + pi.CanRead + "\n");
       sb.Append("可寫:" + pi.CanWrite +"\n");
       sb.Append("屬性:" + pi.Attributes +"\n");
    }

    Console.WriteLine(sb.ToString());
}

輸出如下:

檢視型別 DemoClass的屬性資訊:
--------------------------------------------------
名稱:Name
型別:System.String
可讀:True
可寫:True
屬性:None

從前面的章節可以看到,Name屬性會在編譯後生成Get_Name()和Set_Name()兩個方法,那麼,應該可以利用反射獲取這兩個方法。PropertyInfo類的GetGetMethod()和GetSetMethod()可以完成這個工作,它返回一個MethodInfo物件,封裝了關於方法的資訊,我們會在後面看到。

5.方法資訊 與 MethodInfo 型別

與前面的類似,我們依然可以編寫程式碼來檢視型別的方法資訊。

public static void MethodExplore(Type t) {
    StringBuilder sb = new StringBuilder();
    sb.Append("檢視型別 " + t.Name + "的方法資訊:\n");
    sb.Append(String.Empty.PadLeft(50, '-') + "\n");

    MethodInfo[] methods = t.GetMethods();

    foreach (MethodInfo method in methods) {
       sb.Append("名稱:" + method.Name +"\n");
       sb.Append("簽名:" + method.ToString() + "\n");
       sb.Append("屬性:" + method.Attributes + "\n");
       sb.Append("返回值型別:" + method.ReturnType + "\n\n");
    }

    Console.WriteLine(sb.ToString());
}

與前面類似,MethodInfo 類也有一個Attributes屬性,它返回一個MethodAttribute,MethodAttribute 位標記標明瞭方法的一些屬性,常見的比如Abstract, Static, Virtual,Public, Private 等。

與前面不同的是,Method可以具有引數 和 返回值,MethodInfo 類提供了 GetParameters() 方法獲取 引數物件的陣列,方法的引數都封裝在了 ParameterInfo 型別中。檢視ParameterInfo型別的方法與前面類似,這裡就不再闡述了。

6. ConstructorInfo型別、EventInfo 型別

從名稱就可以看出來,這兩個型別封裝了型別 的建構函式 和 事件資訊,大家都是聰明人,檢視這些型別與之前的方法類似,這裡就不再重複了。

7.小結

本文涉及了反射的最基礎的內容,我們可以利用反射來自頂向下地檢視程式集、模組、型別、型別成員的資訊。反射更強大、也更有意思的內容:遲繫結方法、動態建立型別以後會再講到。

轉自:http://www.tracefact.net/CLR-and-Framework/Reflection-Part2.aspx