農林漁牧網

您現在的位置是:首頁 > 畜牧業

C# 高階程式設計——反射

2022-01-29由 有態度的小青年 發表于 畜牧業

反射型別有哪幾種

C# 反射

反射指程式可以訪問、檢測和修改它本身狀態或行為的一種能力。

程式集包含模組,而模組包含型別,型別又包含成員。反射則提供了封裝程式集、模組和型別的物件。

您可以使用反射動態地建立型別的例項,將型別繫結到現有物件,或從現有物件中獲取型別。然後,可以呼叫型別的方法或訪問其欄位和屬性。

優缺點

優點:

1。 反射提高了程式的靈活性和擴充套件性。  2。 降低耦合性,提高自適應能力。  3。 它允許程式建立和控制任何類的物件,無需提前硬編碼目標類。

缺點:

1。 效能問題:使用反射基本上是一種解釋操作,用於欄位和方法接入時要遠慢於直接程式碼。因此反射機制主要應用在對靈活性和拓展性要求很高的系統框架上,普通程式不建議使用。

2。 使用反射會模糊程式內部邏輯;程式設計師希望在原始碼中看到程式的邏輯,反射卻繞過了原始碼的技術,因而會帶來維護的問題,反射程式碼比相應的直接程式碼更復雜。

一、反射(Reflection)的用途

反射(Reflection)有下列用途:

1。 它允許在執行時檢視特性(attribute)資訊。

2。 它允許審查集合中的各種型別,以及例項化這些型別。

3。 它允許延遲繫結的方法和屬性(property)。

4。 它允許在執行時建立新型別,然後使用這些型別執行一些任務。

(1)

使用Assembly定義和載入程式集,載入在程式集清單中列出模組,以及從此程式集中查詢型別並建立該型別的例項。

(2)

使用Module瞭解包含模組的程式集以及模組中的類等,還可以獲取在模組上定義的所有全域性方法或其他特定的非全域性方法。

(3)

使用ConstructorInfo瞭解建構函式的名稱、引數、訪問修飾符(如public 或private)和實現詳細資訊(如abstract或virtual)等。

(4)

使用MethodInfo瞭解方法的名稱、返回型別、引數、訪問修飾符(如public 或private)和實現詳細資訊(如abstract或virtual)等。

(5)

使用FieldInfo瞭解欄位的名稱、訪問修飾符(如public或private)和實現詳細資訊(如static)等,並獲取或設定欄位值。

(6)

使用EventInfo瞭解事件的名稱、事件處理程式資料型別、自定義屬性、宣告型別和反射型別等,新增或移除事件處理程式。

(7)

使用PropertyInfo瞭解屬性的名稱、資料型別、宣告型別、反射型別和只讀或可寫狀態等,獲取或設定屬性值。

(8)

使用ParameterInfo瞭解引數的名稱、資料型別、是輸入引數還是輸出引數,以及引數在方法簽名中的位置等。

二、檢視元資料

我們已經在上面的章節中提到過,使用反射(Reflection)可以檢視特性(attribute)資訊。

System.Reflection

類的

MemberInfo

物件需要被初始化,用於發現與類相關的特性(attribute)。為了做到這點,您可以定義目標類的一個物件,如下:

System。Reflection。MemberInfo info = typeof(MyClass);

下面的程式演示了這點:

using System; [AttributeUsage(AttributeTargets。All)] public class HelpAttribute : System。Attribute { public readonly string Url; public string Topic // Topic 是一個命名(named)引數 { get { return topic; } set { topic = value; } } public HelpAttribute(string url) // url 是一個定位(positional)引數 { this。Url = url; } private string topic; } [HelpAttribute(“Information on the class MyClass”)] class MyClass { } namespace AttributeAppl { class Program { static void Main(string[] args) { System。Reflection。MemberInfo info = typeof(MyClass); object[] attributes = info。GetCustomAttributes(true); for (int i = 0; i < attributes。Length; i++) { System。Console。WriteLine(attributes[i]); } Console。ReadKey(); } } }

當上面的程式碼被編譯和執行時,它會顯示附加到類 MyClass 上的自定義特性:

HelpAttribute

例項

在本例項中,我們將使用在上一章中建立的 DeBugInfo 特性,並使用反射(Reflection)來讀取 Rectangle 類中的元資料。

using System; using System。Reflection; namespace BugFixApplication { // 一個自定義特性 BugFix 被賦給類及其成員 [AttributeUsage(AttributeTargets。Class | AttributeTargets。Constructor | AttributeTargets。Field | AttributeTargets。Method | AttributeTargets。Property, AllowMultiple = true)] public class DeBugInfo : System。Attribute { private int bugNo; private string developer; private string lastReview; public string message; public DeBugInfo(int bg, string dev, string d) { this。bugNo = bg; this。developer = dev; this。lastReview = d; } public int BugNo { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } } [DeBugInfo(45, “Zara Ali”, “12/8/2012”, Message = “Return type mismatch”)] [DeBugInfo(49, “Nuha Ali”, “10/10/2012”, Message = “Unused variable”)] class Rectangle { // 成員變數 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } [DeBugInfo(55, “Zara Ali”, “19/10/2012”, Message = “Return type mismatch”)] public double GetArea() { return length * width; } [DeBugInfo(56, “Zara Ali”, “19/10/2012”)] public void Display() { Console。WriteLine(“Length: {0}”, length); Console。WriteLine(“Width: {0}”, width); Console。WriteLine(“Area: {0}”, GetArea()); } }//end class Rectangle class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(4。5, 7。5); r。Display(); Type type = typeof(Rectangle); // 遍歷 Rectangle 類的特性 foreach (Object attributes in type。GetCustomAttributes(false)) { DeBugInfo dbi = (DeBugInfo)attributes; if (null != dbi) { Console。WriteLine(“Bug no: {0}”, dbi。BugNo); Console。WriteLine(“Developer: {0}”, dbi。Developer); Console。WriteLine(“Last Reviewed: {0}”, dbi。LastReview); Console。WriteLine(“Remarks: {0}”, dbi。Message); } } // 遍歷方法特性 foreach (MethodInfo m in type。GetMethods()) { foreach (Attribute a in m。GetCustomAttributes(true)) { DeBugInfo dbi = (DeBugInfo)a; if (null != dbi) { Console。WriteLine(“Bug no: {0}, for Method: {1}”, dbi。BugNo, m。Name); Console。WriteLine(“Developer: {0}”, dbi。Developer); Console。WriteLine(“Last Reviewed: {0}”, dbi。LastReview); Console。WriteLine(“Remarks: {0}”, dbi。Message); } } } Console。ReadLine(); } } }

當上面的程式碼被編譯和執行時,它會產生下列結果:

Length: 4。5 Width: 7。5 Area: 33。75 Bug No: 49 Developer: Nuha Ali Last Reviewed: 10/10/2012 Remarks: Unused variable Bug No: 45 Developer: Zara Ali Last Reviewed: 12/8/2012 Remarks: Return type mismatch Bug No: 55, for Method: GetArea Developer: Zara Ali Last Reviewed: 19/10/2012 Remarks: Return type mismatch Bug No: 56, for Method: Display Developer: Zara Ali Last Reviewed: 19/10/2012 Remarks:

三、反射的用法

反射用到的名稱空間:

System。Reflection System。Type System。Reflection。Assembly

反射用到的主要類:

System.Type

類--透過這個類可以訪問任何給定資料型別的資訊。

System.Reflection.Assembly

類--它可以用於訪問給定程式集的資訊,或者把這個程式集載入到程式中。

● System。Type類:

System.Type

類對於反射起著核心的作用。但它是一個抽象的基類,Type有與每種資料型別對應的派生類,我們使用這個派生類的物件的方法、欄位、屬性來查詢有關該型別的所有資訊。  獲取給定型別的Type引用有3種常用方式:

• 使用 C# typeof 運算子。 Type t = typeof(string); • 使用物件GetType()方法。 string s = “grayworm”; Type t = s。GetType(); • 還可以呼叫Type類的靜態方法GetType()。 Type t = Type。GetType(“System。String”);

上面這三類程式碼都是獲取string型別的Type,在取出string型別的Type引用t後,我們就可以透過t來探測string型別的結構了。

string n = “grayworm”; Type t = n。GetType(); foreach (MemberInfo mi in t。GetMembers()) { Console。WriteLine(“{0}\t{1}”, mi。MemberType, mi。Name); }

Type類的屬性:

Name 資料型別名 FullName 資料型別的完全限定名(包括名稱空間名) Namespace 定義資料型別的名稱空間名 IsAbstract 指示該型別是否是抽象型別 IsArray 指示該型別是否是陣列 IsClass 指示該型別是否是類 IsEnum 指示該型別是否是列舉 IsInterface 指示該型別是否是介面 IsPublic 指示該型別是否是公有的 IsSealed 指示該型別是否是密封類 IsValueType 指示該型別是否是值型別

Type類的方法:

GetConstructor(), GetConstructors():返回ConstructorInfo型別,用於取得該類的建構函式的資訊 GetEvent(), GetEvents():返回EventInfo型別,用於取得該類的事件的資訊 GetField(), GetFields():返回FieldInfo型別,用於取得該類的欄位(成員變數)的資訊 GetInterface(), GetInterfaces():返回InterfaceInfo型別,用於取得該類實現的介面的資訊 GetMember(), GetMembers():返回MemberInfo型別,用於取得該類的所有成員的資訊 GetMethod(), GetMethods():返回MethodInfo型別,用於取得該類的方法的資訊 GetProperty(), GetProperties():返回PropertyInfo型別,用於取得該類的屬性的資訊可以呼叫這些成員,其方式是呼叫Type的InvokeMember()方法,或者呼叫MethodInfo, PropertyInfo和其他類的Invoke()方法。

檢視類中的構造方法: NewClassw nc = new NewClassw(); Type t = nc。GetType(); ConstructorInfo[] ci = t。GetConstructors(); //獲取類的所有建構函式 foreach (ConstructorInfo c in ci) //遍歷每一個建構函式 { ParameterInfo[] ps = c。GetParameters(); //取出每個建構函式的所有引數 foreach (ParameterInfo pi in ps) //遍歷並列印所該建構函式的所有引數 { Console。Write(pi。ParameterType。ToString()+“ ”+pi。Name+“,”); } Console。WriteLine(); } 用建構函式動態生成物件: Type t = typeof(NewClassw); Type[] pt = new Type[2]; pt[0] = typeof(string); pt[1] = typeof(string); //根據引數型別獲取建構函式 ConstructorInfo ci = t。GetConstructor(pt); //構造Object陣列,作為建構函式的輸入引數 object[] obj = new object[2]{“grayworm”,“hi。baidu。com/grayworm”}; //呼叫建構函式生成物件 object o = ci。Invoke(obj); //呼叫生成的物件的方法測試是否物件生成成功 //((NewClassw)o)。show(); 用Activator生成物件: Type t = typeof(NewClassw); //建構函式的引數 object[] obj = new object[2] { “grayworm”, “hi。baidu。com/grayworm” }; //用Activator的CreateInstance靜態方法,生成新物件 object o = Activator。CreateInstance(t,“grayworm”,“hi。baidu。com/grayworm”); //((NewClassw)o)。show(); 檢視類中的屬性: NewClassw nc = new NewClassw(); Type t = nc。GetType(); PropertyInfo[] pis = t。GetProperties(); foreach(PropertyInfo pi in pis) { Console。WriteLine(pi。Name); } 檢視類中的public方法: NewClassw nc = new NewClassw(); Type t = nc。GetType(); MethodInfo[] mis = t。GetMethods(); foreach (MethodInfo mi in mis) { Console。WriteLine(mi。ReturnType+“ ”+mi。Name); } 檢視類中的public欄位 NewClassw nc = new NewClassw(); Type t = nc。GetType(); FieldInfo[] fis = t。GetFields(); foreach (FieldInfo fi in fis) { Console。WriteLine(fi。Name); } (http://hi。baidu。com/grayworm) 用反射生成物件,並呼叫屬性、方法和欄位進行操作 NewClassw nc = new NewClassw(); Type t = nc。GetType(); object obj = Activator。CreateInstance(t); //取得私有欄位ID FieldInfo fi = t。GetField(“ID”, BindingFlags。NonPublic | BindingFlags。Instance); //給ID欄位賦值 fi。SetValue(obj, “k001”); //取得MyName屬性 PropertyInfo pi1 = t。GetProperty(“MyName”); //給MyName屬性賦值 pi1。SetValue(obj, “grayworm”, null); PropertyInfo pi2 = t。GetProperty(“MyInfo”); pi2。SetValue(obj, “hi。baidu。com/grayworm”, null); //取得show方法 MethodInfo mi = t。GetMethod(“show”); //呼叫show方法 mi。Invoke(obj, null);

● System。Reflection。Assembly類

Assembly

類可以獲得程式集的資訊,也可以動態的載入程式集,以及在程式集中查詢型別資訊,並建立該型別的例項。  使用

Assembly

類可以降低程式集之間的耦合,有利於軟體結構的合理化。

透過程式集名稱返回Assembly物件 Assembly ass = Assembly。Load(“ClassLibrary831”);透過DLL檔名稱返回Assembly物件 Assembly ass = Assembly。LoadFrom(“ClassLibrary831。dll”);透過型別返回Assembly物件 Assembly editorAssembly = Assembly。GetAssembly(typeof(NewClassw));透過Assembly獲取程式集中類 Type t = ass。GetType(“ClassLibrary831。NewClass”); //引數必須是類的全名透過Assembly獲取程式集中所有的類 Type[] t = ass。GetTypes();

//透過程式集的名稱反射 Assembly ass = Assembly。Load(“ClassLibrary831”); Type t = ass。GetType(“ClassLibrary831。NewClass”); object o = Activator。CreateInstance(t, “grayworm”, “http://hi。baidu。com/grayworm”); MethodInfo mi = t。GetMethod(“show”); mi。Invoke(o, null); //透過DLL檔案全名反射其中的所有型別 Assembly assembly = Assembly。LoadFrom(“xxx。dll的路徑”); Type[] aa = a。GetTypes(); foreach(Type t in aa) { if(t。FullName == “a。b。c”) { object o = Activator。CreateInstance(t); } }