前言 在探究地球內部的結構中,如何做到在地球錶面不用深入地球內部就可以知道內部的構造呢?其實,向地球發射“地震波”。利用這種方式,可以判斷地球放回的情況,大體上,我們也可以斷定地球內部的構造了。 從這個例子中,通過一個對象的外部去瞭解對象內部的構造,都是利用了波的反射功能。而利用這種原理,在編程程式 ...
前言
在探究地球內部的結構中,如何做到在地球錶面不用深入地球內部就可以知道內部的構造呢?其實,向地球發射“地震波”。利用這種方式,可以判斷地球放回的情況,大體上,我們也可以斷定地球內部的構造了。
從這個例子中,通過一個對象的外部去瞭解對象內部的構造,都是利用了波的反射功能。而利用這種原理,在編程程式時,我們如何也可以實現從對象的外部來瞭解對象以及程式集內部的結構功能?在.NET中的反射,不僅可以實現外部對內部的瞭解,也同時可以動態創建出對象並執行其中的方法。
反射是.NET中的重要機制,通過反射,可以在運行時獲得程式或程式集中每一個類型(包括類、結構、委托、介面和枚舉等)的成員和成員的信息。有了反射,即可對每一個類型瞭如指掌。另外我還可以直接創建對象,即使這個對象的類型在編譯時還不知道。
開始
一、使用的命名空間
System.Reflection
System.Type
System.Reflection.Assembly
二、主要的類
System.Type 類--通過這個類可以訪問任何給定數據類型的信息。
System.Reflection.Assembly類--它可以用於訪問給定程式集的信息,或者把這個程式集載入到程式中。
說明
一、System.Type類
System.Type類對反射起著核心的作用。它是一個抽象的基類,Type有與每種數據類型對應的派生類,我們使用這個派生類的對象的方法、欄位、屬性來查找有關該類型的所有信息。
表示類型聲明:類類型、介面類型、數組類型、值類型、枚舉類型、類型參數、泛型類型定義,以及開放或封閉構造的泛型類型。
從Type中解析類型信息:
A、判斷給定類型的引用的常用方式:
1. 使用C# typeof運算符
Type t = typeof(string);
2. 使用對象GetType()方法
string s = "i3yuan"; Type t2 = s.GetType();
3.調用靜態Type類的靜態方法GetType()
Type t3 = Type.GetType("System.String");
以上三種方式獲取類型Type後,可以應用t來探測string裡面的結構
foreach (MemberInfo mi in t.GetMembers()) { Console.WriteLine("{0}/t{1}", mi.MemberType, mi.Name); }
B、Type類屬性:
1.命名空間和類型名
Name 數據類型名
FullName 數據類型的完全限定名(包括命名空間名)
Namespace 定義數據類型的命名空間名
2. 類和委托
Type.IsClass 判斷一個類型是否為類或者委托。符合條件的會有普通的類(包括泛型)、抽象類(abstract class)、委托(delegate)
3. 是否泛型
Type.IsGenericType 屬性可以判斷類或委托是否為泛型類型。
Type.IsGenericTypeDefinition 屬性可以判斷Type是否是未綁定參數類型的泛型類型。
Type.IsConstructedGenericType 屬性判斷是否可以此Type創建泛型實例。
4.訪問修飾符
Type.IsPublic 判斷該類型是否是公有的
Type.IsNotPublic
5.密封類、靜態類型、抽象類
Type.IsSealed 判斷該類型是否是密封類,密封類不能被繼承
IsAbstract 指示該類型是否是抽象類型
6. 值類型
Type.IsValueType 判斷一個 Type 是否為值類型,簡單值類型、結構體、枚舉,都符合要求。
Type.IsEnum 判斷該類型是否是枚舉
Type.IsPrimitive 判斷Type是否為基礎類型
7.介面
Type.IsInterface 判斷該類型是否是介面
8.數組
IsArray 判斷該類型是否是數組,GetArrayRank()
獲取數組的維數。
從Type類解析類型成員結構
一個類由以下一個或多個成員組成:
成員類型 | 說明 |
---|---|
PropertyInfo | 類型的屬性信息 |
FieldInfo | 類型的欄位信息 |
ConstructorInfo | 類型的構造函數信息 |
MethodInfo | 類型的方法 |
ParameterInfo | 構造函數或方法的參數 |
EventInfo | 類型的事件 |
C、Type類的方法
public class MySqlHelper { public MySqlHelper() { Console.WriteLine("{0}被構造", this.GetType().Name); } public void Query() { Console.WriteLine("{0}.Query", this.GetType().Name); } }
GetConstructor(), GetConstructors():返回ConstructorInfo類型,用於取得該類的構造函數的信息
MySqlHelper dBHelper = new MySqlHelper(); Type type = dBHelper.GetType(); ConstructorInfo[] constructorInfos= type.GetConstructors(); foreach (var item in constructorInfos) { ParameterInfo[] parameterInfos = item.GetParameters(); foreach (var info in parameterInfos) { Console.WriteLine("查看:" + info.ParameterType.ToString() + " " + info.Name); } }
GetEvent(), GetEvents():返回EventInfo類型,用於取得該類的事件的信息
GetField(), GetFields():返回FieldInfo類型,用於取得該類的欄位(成員變數)的信息
MySqlHelper nc = new MySqlHelper(); Type t = nc.GetType(); FieldInfo[] fis = t.GetFields(); foreach (FieldInfo fi in fis) { Console.WriteLine(fi.Name); }
GetInterface(), GetInterfaces():返回InterfaceInfo類型,用於取得該類實現的介面的信息
GetMember(), GetMembers():返回MemberInfo類型,用於取得該類的所有成員的信息
string n = "i3yuan"; Type t = n.GetType(); foreach (MemberInfo mi in t.GetMembers()) { Console.WriteLine("{0}/t{1}",mi.MemberType,mi.Name); }
GetMethod(), GetMethods():返回MethodInfo類型,用於取得該類的方法的信息
MySqlHelper dBHelper= new MySqlHelper(); Type t = dBHelper.GetType(); MethodInfo[] mis = t.GetMethods(); foreach (MethodInfo mi in mis) { Console.WriteLine(mi.ReturnType+" "+mi.Name); }
GetProperty(), GetProperties():返回PropertyInfo類型,用於取得該類的屬性的信息
MySqlHelper dBHelper= new MySqlHelper(); Type t = dBHelper.GetType(); PropertyInfo[] pis = t.GetProperties(); foreach(PropertyInfo pi in pis) { Console.WriteLine(pi.Name); }
可以調用這些成員,其方式是調用Type的InvokeMember()方法,或者調用MethodInfo, PropertyInfo和其他類的Invoke()方法。
用反射生成對象,並調用屬性、方法和欄位進行操作
//獲取類型信息 MySqlHelper dbHelper=new MySqlHelper(); Type type=dbHelper.GetType //創建對象實例化 object DbHelper = Activator.CreateInstance(type); //類型轉換 IDBHelper iDBHelper = (IDBHelper)DbHelper; //方法調用 iDBHelper.Query();
二、System.Reflection.Assembly類
Assembly類可以獲得程式集的信息,也可以動態的載入程式集,以及在程式集中查找類型信息,並創建該類型的實例。使用Assembly類可以降低程式集之間的耦合,有利於軟體結構的合理化
1. System.Reflection
用於訪問給定程式集的信息,或者把這個程式集載入到程式中。可以讀取並使用metadata
方法調用過程:
1.載入DLL ; 2. 獲取類型信息 ;3. 創建對象類型 4. 類型轉換 5. 方法調用
Assembly assembly = Assembly.Load("Yuan.DB.MySql");//dll名稱無尾碼 從當前目錄載入 1 載入dll //完整路徑的載入 可以是別的目錄 載入不會錯,但是如果沒有依賴項,使用的時候會錯 Type type = assembly.GetType("Yuan.DB.MySql.MySqlHelper");//2 獲取類型信息 object oDBHelper = Activator.CreateInstance(type);//3 創建對象 //oDBHelper.Query();//oDBHelper是objec不能調用,但實際上方法是有的 編譯器不認可 IDBHelper iDBHelper = (IDBHelper)oDBHelper;//4 類型轉換 iDBHelper.Query();//5 方法調用
方法二:通過程式集的名稱反射
Assembly assembly = Assembly.Load("Yuan.DB.MySql");//dll名稱無尾碼 從當前目錄載入 1 載入dll Type type = assembly.GetType("Yuan.DB.MySql.MySqlHelper");//2 獲取類型信息 object oDBHelper = Activator.CreateInstance(type);//3 創建對象 MethodInfo mi=oDBHelper.GetMethod("Query"); //獲取方法 mi.Invoke(oDBHelper,null);//5 調用
總結
1. 作為一個開發人員,在每天都會應用到反射,使用的時候,會反射當前程式的元數據,將所有的方法,類等信息都全部顯示出來,以便開發人員使用,大大的提高了效率
2. 同時反射提高了程式的靈活性和拓展性,降低耦合,動態載入,允許控制和實現任何類的對象。
3. 當然了,也存在弊端,寫起來複雜,也存在性能問題,用於欄位和方法接入時要遠慢於直接代碼。
參考 文檔 和 《C#圖解教程》
註:搜索關註公眾號【雜學谷】--回覆【C#圖解】,可獲取 C#圖解教程文件