[.net 物件導向程式設計進階] (20) 反射(Reflection)(上)利用反射技術實現動態程式設計
本節導讀:本節主要介紹什麼是.NET反射特性,.NET反射能為我們做些什麼,最後介紹幾種常用的反射的實現方法,通過對反射性特的瞭解,可以設計出非常有用的基於反射的程式設計模式。
讀前必備:
[.net 物件導向程式設計基礎] (10) 類的成員(欄位、屬性、方法)
1.什麼是.NET反射?
反射是.NET一個重要的特性,《CLR via C#》一書中對.NET反射的解釋為:在我們應用程式中使用後設資料來表示儲存。編譯程式集或模組時,編譯器會建立一個型別定義表、一個欄位定義表、一個方法定義表以及其它表。而我們如果想動態呼叫這些後設資料表(或說是解析這些表),或說是為這些後設資料建立一個物件模型,這個過程就是反射。
簡單通俗的說,就是動態呼叫編譯過的程式集中的屬性,欄位,方法等的過程,就是反射。
反射在.NET,通過System.Reflection名稱空間中的類來實現。
2.反射能為我們做些什麼?
這個問題是我們學習反射的重點,總得知道學習它的好處,才會繼續把本文看下去。
反射特性,確實是.NET一個非常重要且有用的特性。
A.列舉型別成員
B.例項化新物件
C.執行物件成員
D.查詢型別資訊
E.查詢程式集資訊
F.檢查應用於某種型別的自定義特性
G.建立和編譯新的程式集
H.簡化執行時而非編譯時繫結的資料的操作。(C# 4.0以後新功能)
此外.NET新版本中允許泛型上的反射.
以上是反射的基本特性,參考了《C#本質論》和《C#高階程式設計》
基於上面的基本特性,我們可以設計出很多非常實用的程式設計模式。
下面列舉幾種基於反射設計模式下的例項:
A.利用反射創動態建立程式集的API文件。基於反射允許列舉程式集中型別及成員的特性,我們可以通過反射獲取已編譯的程式集中的欄位方法屬性事件和他們的XML註釋。從而動態建立程式集的API文件;
B.非常常用的反射工廠模式。反射工廠模式在設計模式中比較容易理解,也比較簡單。很多程式碼生成器中就利用這種設計模式完成不同資料庫的反射呼叫。比如我們有MsSql、MySql、Oracle這三種資料庫,在專案設計中,我們有可能隨時換另一種資料庫(當然這只是假設),因此需要同時實現這三種資料庫的基礎增刪改查的類即資料訪問類。我們要切換資料庫的時候,只需要在config中更改資料庫型別,其他的工作交給反射工廠類去動態呼叫編譯好的程式集中對應的資料庫訪問方法。
如果沒有理解也沒關係,這裡只是說明一下反射的應用例項,以便於更有信心的學習反射。反射在設計模式中的應還有很多,這裡不再列舉。
3.反射應用基礎
上面說了這麼多,無非就是先讓我們理解反射能為我們做些什麼,下面進入正題,說一下反射的程式碼實現。
下面主要介紹反射的核心類及類成員
反射的名稱空間:System.Reflection
反射的類大多都在這個名稱空間中。
主要的類: System.Type
這個類是反射的核心,其屬性方法可以得到執行時的資訊。
Type類派生於System.Reflection.MemberInfo抽象類
MemberInfo類中的只讀屬性 |
||
屬性 |
描述 |
備註 |
Type DeclaringType |
獲取宣告該成員的類或介面的型別 |
|
MemberTypes MemberType |
獲取成員的型別,這個值用於指示該成員是欄位、方法、屬性、事件、或建構函式 |
這是一個列舉,它定義了用於表示不同成員的型別值。這些值包括:MemberTypes.Constructor, MemberTypes.Method, MemberTypes.Field, MemberTypes.Event, MemberTypes.Property。因此可以通過檢查MemberType屬性來確定成員的型別,例如,在MemberType屬性的值為MemberTypes.Method時,該成員為方法 |
Int MetadataToken |
獲取與特定後設資料相關的值 |
|
Module Module |
獲取一個代表反射型別所在模組(可執行檔案)的Module物件 |
|
String Name |
成員的名稱 |
|
Type ReflectedType |
反射的物件型別 |
|
Type類的只讀屬性 |
|
屬性 |
描述 |
Assembly Assembly |
獲取指定型別的程式集 |
TypeAttributes Attributes |
獲取制定型別的特性 |
Type BaseType |
獲取指定型別的直接基型別 |
String FullName |
獲取指定型別的全名 |
bool IsAbstract |
如果指定型別是抽象型別,返回true |
bool IsClass |
如果指定型別是類,返回true |
string Namespace |
獲取指定型別的名稱空間 |
Type類的方法 |
|
方法 |
描述 |
ConstructorInfo[] GetConstructors() |
獲取指定型別的建構函式列表 |
EventInfo[] GetEvents(); |
獲取指定型別的時間列 |
FieldInfo[] GetFields(); |
獲取指定型別的欄位列 |
Type[] GetGenericArguments(); |
獲取與已構造的泛型型別繫結的型別引數列表,如果指定型別的泛型型別定義,則獲得型別形參。對於正早構造的型別,該列表就可能同時包含型別實參和型別形 |
MethodInfo[] GetMethods(); |
獲取指定型別的方法列表 |
PropertyInfo[] GetProperties(); |
獲取指定型別的屬性列表e |
MemberInfo[] GetMembers(); |
獲取指定型別的成員列表 |
反射還有很多類,這裡不一一介紹,詳細可以查閱MSDN:
https://msdn.microsoft.com/zh-cn/library/system.reflection.aspx
4.反射例項
下面通過一個例項來學習一下反射最基本的使用方法。
建立一個解決方案,包含兩個專案,專案ClassLibrary生成一個DLL(包含兩個類),另一個專案是ReflectionTestGet,用於反射呼叫類ClassLibrary
第一個專案的兩個類如下:
MartialArtsMaster.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClassLibrary { /// <summary> /// 類:武林高手 /// MartialArtsMaster /// </summary> class MartialArtsMaster { /// <summary> /// 級別 /// </summary> public int _level = 9; /// <summary> /// 編號 /// </summary> public int Id { get; set; } /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 年齡 /// </summary> public int Age { get; set; } /// <summary> /// 門派 /// </summary> public string Menpai { get; set; } /// <summary> /// 武學 /// </summary> public string Kungfu { get; set; } /// <summary> /// 級別 /// </summary> public int Level { get { return _level; } set { _level = value; } } /// <summary> /// 攻擊 /// </summary> /// <param name="kungfu"></param> /// <returns></returns> public string Attack(string kungfu) { return "使用用了功夫:" + kungfu; } public string Kill(string kungfu, string name) { return "使用用了功夫:" + kungfu + "擊殺了" + name; } } }
Person.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClassLibrary { /// <summary> /// 類:人 /// </summary> class Person { public string gender { get; set; } public string race { get; set; } public string Country { get; set; } public string Eat(string strCountry) { switch (strCountry) { case "美國": return "愛吃西餐"; case "韓國": return "愛吃泡菜"; default: return "不知道"; } } } }
第二個專案呼叫如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reflection; namespace ReflectionTestGet { class Program { static void Main(string[] args) { Assembly asm = Assembly.LoadFrom("ClassLibrary.dll"); //載入指定的程式集 Type[] alltype = asm.GetTypes(); //獲取程式集中的所有型別列表 foreach (Type calssName in alltype) { Console.WriteLine("載入程式的集類名:"+ calssName.Name); //列印出程式集所有類 foreach (var field in calssName.GetFields()) Console.WriteLine(calssName.Name+"欄位有:" + field.Name); //列印出程式集所有欄位,注意只能獲取公有欄位 foreach (var pro in calssName.GetProperties()) Console.WriteLine(calssName.Name + "屬性有:" + pro.Name); //列印出程式集所有屬性 foreach (var met in calssName.GetMethods()) Console.WriteLine(calssName.Name + "方法有:" + met.Name); //列印出程式集所有方法 } Console.ReadKey(); } } }
執行結果如下:
5.本節要點:
本節主要介紹和反射的用途及反射的基本操作類及屬性方法,下節繼續深入介紹如何將反射技術應用於實際專案之中。
==============================================================================================
<如果對你有幫助,記得點一下推薦哦,如有
有不明白或錯誤之處,請多交流>
<對本系列文章閱讀有困難的朋友,請先看《.net 物件導向程式設計基礎》>
<轉載宣告:技術需要共享精神,歡迎轉載本部落格中的文章,但請註明版權及URL>
==============================================================================================