1..NET反射的概述
.NET反射是審查元數據並動態收集關於它的類型信息的能力。
應用程序結構分為應用程序域—程序集—模塊—類型—成員幾個層次,公共語言運行庫加載器管理應用程序域。這些域在擁有相同應用程序范圍的對象周圍形成了確定邊界。
這種管理包括將每個程序集加載到相應的應用程序域以及控制每個程序集中類型層次結構的內存布局。程序集包含模塊,而模塊包含類型,類型又包含成員,反射則提供了封裝程序集、模塊和類型的對象。
我們可以使用反射動態地創建類型的實例,將類型綁定到現有對象或從現有對象中獲取類型,然后調用類型的方法或訪問其字段和屬性。
反射的層次模型:
2..NET反射的作用和應用
(1).可以使用反射動態地創建類型的實例,將類型綁定到現有對象,或從現有對象中獲取類型;
(2).應用程序需要在運行時從某個特定的程序集中載入一個特定的類型,以便實現某個任務時可以用到反射;
(3).反射主要應用與類庫,這些類庫需要知道一個類型的定義,以便提供更多的功能;
(4).在.NET中實現工廠模式會使用反射較多;
(5).在JavaScript等語言編譯器使用反射來構造符號表;
(6).反射也可用於創建稱為類型瀏覽器的應用程序,使用戶能夠選擇類型,然后查看有關選定類型的信息;
(7).System.Runtime.Serialization命名空間中的類使用反射來訪問數據並確定要永久保存的字段;
(8).System.Runtime.Remoting命名空間中的類通過序列化來間接地使用反射。
3..NET反射的性能
使用反射來調用類型或者觸發方法,或者訪問一個字段或者屬性時clr 需 要做更多的工作:校驗參數,檢查權限等等,所以速度是非常慢的。 所以盡量不要使用反射進行編程,對於打算編寫一個動態構造類型(晚綁定)的應用程序,可以采取以下的幾種方式進行代替:
(1).通過類的繼承關系
讓該類型從一個編譯時可知的基礎類型派生出來,在運行時生成該類 型的一個實例,將對其的引用放到其基礎類型的一個變量中,然后調用該基礎類型的虛方法。
(2).通過接口實現
在運行時,構建該類型的一個實例,將對其的引用放到其接口類型的一個變量中,然后調用該接口定義的虛方法。
(3).通過委托實現
讓該類型實現一個方法,其名稱和原型都與一個在編譯時就已知的委托相符。 在運行時先構造該類型的實例,然后在用該方法的對象及名稱構造出該委托的實例,接着通過委托調用你想要的方法。這個方法相對與前面兩個方法所作的工作要多一些,效率更低一些。
提高反射的性能:反射的性能損失主要來源於比較類型、遍歷成員、調用成員三種情形,其中比較類型耗時最小。 調用成員耗時最多,所以盡量減少采用成員動態調用等反射方式可以提高應用程序性能。除此之外,采取后期綁定、避免將反射方法放到循環內產生放大效應等辦法均可提升反射性能。
4..NET反射常用類型
System.reflection命名空間下常用類型,允許你反射(解析)這些元數據表的代碼和反射相關的命名空間(可以通過這幾個命名空間訪問反射信息):
(1).System.Reflection.Assembly
使用Assembly定義和加載程序集,加載在程序集清單中列出模塊,以及從此程序集中查找類型並創建該類型的實例。可以使用Assembly.Load和Assembly.LoadFrom方法動態地加載程序集。
(2).System.Reflection.Module
使用Module了解包含模塊的程序集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。 (3).System.Reflection.ConstructorInfo
使用ConstructorInfo了解構造函數的名稱、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法來調用特定的構造函數。
(4).System.Reflection.MethodInfo
使用MethodInfo了解方法的名稱、返回類型、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法來調用特定的方法。
(5).System.Reflection.FieldInfo
使用FieldInfo了解字段的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,並獲取或設置字段值。
(6).System.Reflection.EventInfo
使用EventInfo了解事件的名稱、事件處理程序數據類型、自定義屬性、聲明類型和反射類型等,添加或移除事件處理程序。
(7).System.Reflection.PropertyInfo
使用PropertyInfo了解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,獲取或設置屬性值。
(8).System.Reflection.ParameterInfo
使用ParameterInfo了解參數的名稱、數據類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
System.Reflection.Emit命名空間的類提供了一種特殊形式的反射,可以在運行時構造類型。
(9).System.Type
System.Type類是一個抽象類,代表公用類型系統中的一種類型。這個類使您能夠查詢類型名、類型中包含的模塊和名稱空間、以及該類型是一個數值類型還是一個引用類型。 System.Type類使您能夠查詢幾乎所有與類型相關的屬性,包括類型訪問限定符、類型是否、類型的COM屬性等等。
(10).System.Activator
Activator類支持動態創建.NET程序集和COM對象。可以通過CreateComInstanceFrom、CreateInstance、CreateInstanceFrom、GetObject四個靜態方法加載COM對象或者程序集,並能創建指定類型的實例。
使用注意:
(1).Load方法:極力推薦的一種方法,Load 方法帶有一個程序集標志並載入它,Load 將引起CLR把策略應用到程序集上。 先后在全局程序集緩沖區,應用程序基目錄和私有路徑下面查找該程序集,如果找不到該程序集系統拋出異常。
(2).LoadFrom方法:傳遞一個程序集文件的路徑名(包括擴展名),CLR會載入您指定的這個程序集,傳遞的這個參數不能包含任何關於版本號的信息,區域性,和公鑰信息,如果在指定路徑找不到程序集拋出異常。
(3).LoadWithPartialName:永遠不要使用這個方法,因為應用程序不能確定再在載入的程序集的版本。該方法的唯一用途是幫助那些在.Net框架的測試環節使用.net 框架提供的某種行為的客戶,這個方法將最終被拋棄不用。
5..NET反射示例Demo
(1).代碼結構
(2).TestClassLib類庫的Arithmetic類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClassLib { /// <summary> /// 定義示例接口Iwel /// </summary> public interface Iwel { string Print(); } /// <summary> /// 構造測試數據類 /// </summary> public class Arithmetic:Iwel { //定義私有變量 private int numOne; private int numTwo; #region 構造函數測試 //不帶參數的構造函數 public Arithmetic() { } //帶參數構造函數,初始化numOne和numTwo public Arithmetic(int intNumOne, int intNumTwo) { this.numOne = intNumOne; this.numTwo = intNumTwo; } #endregion #region 屬性測試 //公共屬性訪問NumOne public int NumOne { get { return numOne; } set { numOne = value; } } //公共屬性 訪問NumTwo public int NumTwo { get { return numTwo; } set { numTwo = value; } } #endregion #region 方法測試 /// <summary> /// 私有非靜態不帶參數的方法 /// </summary> /// <returns></returns> private string Add() { return "Add()方法是一個私有不帶參數的方法"; } /// <summary> /// 私有靜態帶參數無返回值的方法 /// </summary> /// <param name="intNumOne"></param> private static void Multiplcation(int intNumOne) { Console.WriteLine("Multiplication()方法是一個私有不帶參數無返回值的靜態方法"); } /// <summary> /// 私有非靜態帶參數的方法 /// </summary> /// <param name="intNumOne"></param> /// <param name="intNumTwo"></param> private void Subtration(int intNumOne, int intNumTwo) { string strMeesage = "{0}-{1}={2} Subtration(int intNumOne, int intNumTwo)方法是一個私有帶參數的方法"; Console.WriteLine(strMeesage, intNumOne, intNumTwo, intNumOne - intNumTwo); } /// <summary> /// 公有非靜態不帶參數的方法 /// </summary> /// <returns></returns> public void Write() { Console.WriteLine( "Write() 是一個共有的不帶參數無返回值的方法"); } /// <summary> /// 公有靜態帶參數的方法 /// </summary> /// <param name="intNumOne"></param> /// <param name="intNumTwo"></param> public static string Multiplcation(int intNumOne, int intNumTwo) { string strMessage = "{0}*{1}={2} Multiplcation(int intNumOne,int intNumTwo)方法是一個公有的靜態帶參數的方法"; strMessage = string.Format("{0}*{1}={2}", intNumOne, intNumTwo, intNumOne * intNumTwo); return strMessage; } /// <summary> /// 公有非靜態帶參數的方法 /// </summary> /// <param name="intNumOne"></param> /// <param name="intNumTwo"></param> /// <returns></returns> public string Add(int intNumOne,int intNumTwo) { string strMessage = string.Format("{0}+{1}={2}", intNumOne, intNumTwo, intNumOne + intNumTwo); return strMessage; } #endregion /// <summary> /// 集成接口方法 /// </summary> /// <returns></returns> public string Print() { return " Print() 繼承接口方法"; } } }
(3).TestClassLib類庫的TestClass類

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClassLib { public class TestClass { //模擬測試 public class Dialog : DialogInvoker { } public virtual string TestMethod(string pp) { return pp; } } }
(4).TestClassLib類庫的DialogInvoker類

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClassLib { public class DialogInvoker { public int TestInt; public void RegisterWithKey(string strOne,string strTwo) { Console.WriteLine("Test組合類:"+strOne+","+strTwo); } } }
(5).TestClassLib類庫的TestDialog類

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClassLib { public class TestDialog { //模擬測試 public void OnInit() { new TestClass.Dialog().RegisterWithKey("PP", "TT"); } } }
(6).ReflectTest的Program.cs代碼調用測試
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using TestClassLib; namespace ReflectTest { class Program { //定義委托測試 delegate void TestDelegate(int intNumOne,int intNumTwo); static void Main(string[] args) { SimpleTest();//簡單測試 TestArithmetic();//分析測試 TestDialog();//模擬測試 Console.ReadLine(); } /// <summary> /// 基本簡單測試 /// </summary> static void SimpleTest() { //System.Reflection.Assembly ass = Assembly.Load("TestClassLib"); //加載DLL //System.Type t = ass.GetType("TestClassLib.TestChildrenClass");//獲得類型 string path = "TestClassLib.TestClass" + "," + "TestClassLib";//命名空間.類型名,程序集 Type testType = Type.GetType(path);//加載類型 object objType = System.Activator.CreateInstance(testType);//創建實例 System.Reflection.MethodInfo methodReflection = testType.GetMethod("TestMethod");//獲得方法 object str = methodReflection.Invoke(objType, new object[] { "MM" });//調用方法 Console.WriteLine("簡單測試輸出:"+str.ToString()); } /// <summary> /// 反射分析測試 /// </summary> static void TestArithmetic() { string strAssembly = "TestClassLib";//程序集DLL string strReflectClass = "TestClassLib.Arithmetic";//命名空間.類型 string strMethodAdd = "Add";//調用方法Add名稱 string strMethodWrite = "Write";//調用方法Write名稱 string strMethodMul = "Multiplcation";//調用方法Multiplcation名稱 Assembly AssTestClassLib = Assembly.Load(strAssembly);//得到程序集 Type ArithmeticClass = AssTestClassLib.GetType(strReflectClass);;//得到具體類型 //Type ArithmeticClass = Type.GetType(strReflectClass + "," + strAssembly);//創建對象也可以使用:命名空間.類型名,程序集 #region 獲取程序集下的信息 Console.WriteLine("\n得到"+strAssembly+"中所有類:" ); foreach (Type type in AssTestClassLib.GetTypes()) { Console.WriteLine(type.Name+"是"+strAssembly+"命名空間下的對象"); } Console.WriteLine("\n得到"+strAssembly+".dll中的模塊集:"); Module[] modules = AssTestClassLib.GetModules(); foreach (Module module in modules) { Console.WriteLine(module.Name+"是"+strAssembly+"下的模塊集"); } #endregion #region 獲取指定類下的信息 Console.WriteLine("\n具體類型是"+ArithmeticClass.Name); Console.WriteLine("{0}是不是Public類型{1}",ArithmeticClass,ArithmeticClass.IsPublic); Console.WriteLine("{0}是不是Privet類型{1}",ArithmeticClass,ArithmeticClass.IsPrimitive); Console.WriteLine("\n得到" + ArithmeticClass + "類下的構造函數:"); ConstructorInfo [] constructorInfos = ArithmeticClass.GetConstructors(); foreach (ConstructorInfo constructor in constructorInfos) { Console.WriteLine(constructor); } Console.WriteLine("\n得到"+ArithmeticClass+"類下的所有屬性:"); PropertyInfo[] propertyInfos = ArithmeticClass.GetProperties(); foreach (PropertyInfo prop in propertyInfos) { Console.WriteLine(prop.Name+"是"+ArithmeticClass + "類下的屬性"); } Console.WriteLine("\n得到"+ArithmeticClass+"類下所有的接口:"); Type[] typeInterfaces = ArithmeticClass.GetInterfaces(); foreach (Type typeInterface in typeInterfaces) { Console.WriteLine(typeInterface.Name+"是"+ArithmeticClass+"類下的接口"); } #region 獲取所有方法的信息 Console.WriteLine("\n得到"+ArithmeticClass+"類下所有方法:"); //查找私有方法 MethodInfo[] methodPrivates = ArithmeticClass.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic); foreach (MethodInfo method in methodPrivates) { Console.WriteLine("私有方法 方法名稱:{0} 方法信息:{1}",method.Name,method); } //查找公有方法 MethodInfo[] methodPublics = ArithmeticClass.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo method in methodPublics) { Console.WriteLine("公有方法 方法名稱:{0} 方法信息:{1}", method.Name, method); } //查找私有靜態方法 MethodInfo[] methodStaticPrivates = ArithmeticClass.GetMethods(BindingFlags.NonPublic | BindingFlags.Static); foreach (MethodInfo method in methodStaticPrivates) { Console.WriteLine("私有靜態方法 方法名稱:{0} 方法信息:{1}", method.Name, method); } //查找公有靜態方法 MethodInfo[] methodStaticPublics = ArithmeticClass.GetMethods(BindingFlags.Public | BindingFlags.Static); foreach (MethodInfo method in methodPrivates) { Console.WriteLine("公有靜態方法 方法名稱:{0} 方法信息:{1}", method.Name, method); } #endregion #endregion #region 創建對象實例 Console.WriteLine("\n創建對象實例:"); int num1 = 3, num2 = 5;//定義參數 //創建一個不帶參數的實例 object obj = Activator.CreateInstance(ArithmeticClass, null); //公有非靜態帶參數和返回值的方法調用 MethodInfo methodAdd = ArithmeticClass.GetMethod(strMethodAdd); object[] addParams = new object[] {num1,num2 }; string strReturnAdd=methodAdd.Invoke(obj, addParams).ToString(); Console.WriteLine("創建{0}類,調用公有非靜態{1}方法,傳入參數{2}和{3},返回值:{4}",ArithmeticClass, strMethodAdd, num1, num2, strReturnAdd); //私有非靜態無參無返回值方法調用 MethodInfo methodStaticAdd = ArithmeticClass.GetMethod(strMethodAdd, BindingFlags.Instance | BindingFlags.NonPublic); string strStaticAdd=methodStaticAdd.Invoke(obj,null).ToString(); Console.WriteLine("創建{0}類,調用私有非靜態{1}方法,返回值:{2}:", ArithmeticClass, strMethodAdd, strStaticAdd); //公有非靜態無參數無返回值方法調用 MethodInfo methodWite = ArithmeticClass.GetMethod(strMethodWrite, BindingFlags.Instance | BindingFlags.Public); Console.WriteLine("創建{0}類,調用公有非靜態{1}方法,無參數無返回值:", ArithmeticClass, strMethodWrite, methodWite.Invoke(obj, null)); //公有靜態帶參數有返回值的方法調用 MethodInfo methodMultiplcation = ArithmeticClass.GetMethod(strMethodMul, BindingFlags.Public | BindingFlags.Static); object[] multParams = new object[] { num1, num2 }; string multReturn = methodMultiplcation.Invoke(obj, multParams).ToString(); //string multReturn = methodMultiplcation.Invoke(null, multParams).ToString();//調用靜態方法也可以轉入null對象 Console.WriteLine("創建{0}類,調用公有靜態{1}方法,傳入參數{2}和{3},返回值:{4}", ArithmeticClass, strMethodMul, num1, num2, multReturn); //測試InvokeMember方法 object objReturn= ArithmeticClass.InvokeMember(strMethodAdd, BindingFlags.InvokeMethod, null, obj,new object[]{num1,num2}); Console.WriteLine("測試InvokeMember方法:創建{0}類,調用公有靜態{1}方法,傳入參數{2}和{3},返回值:{4}:" , ArithmeticClass, strMethodAdd, num1, num2, objReturn.ToString()); #endregion //動態創建委托 Console.WriteLine("\n動態創建委托:"); TestDelegate testDel = (TestDelegate)Delegate.CreateDelegate(typeof(TestDelegate), obj, "Subtration"); testDel(9,3); } /// <summary> /// 模擬測試 /// </summary> static void TestDialog() { string strAssembly = "TestClassLib";//程序集DLL string strDialogReflect = "TestClassLib.TestDialog"; string strDialogInvokerReflect = "TestClassLib.DialogInvoker"; string strDialogNestedTypeReflect = "TestClassLib.TestClass";//嵌套如何關系類,命名空間.類 string strNestedType = "Dialog";//嵌套組合類 string strDialogMethod = "OnInit"; string strDialogInvokerMethod = "RegisterWithKey"; Console.WriteLine("\n模擬測試:"); Assembly assemblyTest = Assembly.Load(strAssembly); //間接調用 Type typeDialog = assemblyTest.GetType(strDialogReflect); object objDialog = Activator.CreateInstance(typeDialog); MethodInfo methodDialog = typeDialog.GetMethod(strDialogMethod); methodDialog.Invoke(objDialog, null); //直接調用 Type typeDialogInvoker = assemblyTest.GetType(strDialogInvokerReflect); object objDialogInvoker = Activator.CreateInstance(typeDialogInvoker); MethodInfo methodDialogInvoker = typeDialogInvoker.GetMethod(strDialogInvokerMethod); methodDialogInvoker.Invoke(objDialogInvoker, new object[] { "MM", "KK" }); //使用嵌套組合關系調用(這個更實用) var typeTestClass =assemblyTest.GetType(strDialogNestedTypeReflect); //var typeTestClass = Assembly.GetExecutingAssembly().GetType(strDialogNestedTypeReflect);//如果在同一個程序集下可以使用這種方式 var testClass= Activator.CreateInstance(typeTestClass);//創建本類 var typeNestedDialog= typeTestClass.GetNestedType(strNestedType);//獲取嵌套組合類 var dialogClass = Activator.CreateInstance(typeNestedDialog);//創建嵌套組合類 var methodInfo = dialogClass.GetType().GetMethod(strDialogInvokerMethod, BindingFlags.Instance | BindingFlags.Public); methodInfo.Invoke(dialogClass, new object[] {"TT","KK" }); } } }
6.運行效果如下:
7.反射實現工廠模式
public partial class 反射 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string typeName = typeof(TestClass).AssemblyQualifiedName; ITestInterface iface = RawGenericFactory.Create<ITestInterface>(typeName); string result = iface.doSomething(); Response.Write(result); } } public static class RawGenericFactory { public static T Create<T>(string typeName) { //Activator.CreateInstance 反射 根據程序集創建借口或者類 //Type.GetType() 根據名稱獲得程序集信息 //typeof(ConcertProduct).AssemblyQualifiedName //_iproduct.GetType().AssemblyQualifiedName return (T)Activator.CreateInstance(Type.GetType(typeName)); } } public interface ITestInterface { string doSomething(); } public class TestClass : ITestInterface { public int Id { get; set; } public override string ToString() { return Id.ToString(); } public string doSomething() { return "ok"; } }
8.反射實現自定義ORM框架
[Orm.Table("TestORM")] public class TestORM { [Orm.Colum("Id",DbType.Int32)] public int Id { get; set; } [Orm.Colum("UserName", DbType.String)] public string UserName { get; set; } [Orm.Colum("Password", DbType.String)] public string Password { get; set; } [Orm.Colum("CreatedTime", DbType.DateTime)] public DateTime CreatedTime { get; set; } } protected void Button3_Click(object sender, EventArgs e) { TestORM t = new TestORM() { Id=1, UserName="binfire", Password="xxx", CreatedTime=DateTime.Now }; Orm.OrmHelp h=new Orm.OrmHelp(); h.Insert(t); } namespace Orm { [AttributeUsageAttribute(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class TableAttribute : Attribute { //保存表名的字段 private string _tableName; public TableAttribute() { } public TableAttribute(string tableName) { this._tableName = tableName; } /// /// 映射的表名(表的全名:模式名.表名) /// public string TableName { set { this._tableName = value; } get { return this._tableName; } } } [AttributeUsageAttribute(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public class ColumAttribute : Attribute { private string _columName; private DbType _dbType; public ColumAttribute() { } public ColumAttribute(string columName) : this() { this._columName = columName; } public ColumAttribute(string columName, DbType dbType) : this(columName) { this._dbType = dbType; } //列名 public virtual string ColumName { set { this._columName = value; } get { return this._columName; } } //描述一些特殊的數據庫類型 public DbType DbType { get { return _dbType; } set { _dbType = value; } } } public class OrmHelp { public void Insert(object table) { Type type = table.GetType(); //定義一個字典來存放表中字段和值的對應序列 Dictionary<string,string> columValue = new Dictionary<string,string>(); StringBuilder SqlStr = new StringBuilder(); SqlStr.Append("insert into "); //得到表名子 TableAttribute temp = (TableAttribute)type.GetCustomAttributes(typeof(TableAttribute), false).First(); SqlStr.Append(temp.TableName); SqlStr.Append("("); PropertyInfo[] Propertys = type.GetProperties(); foreach (var item in Propertys) { object[] attributes = item.GetCustomAttributes(false); foreach (var item1 in attributes) { //獲得相應屬性的值 string value = table.GetType().InvokeMember(item.Name, System.Reflection.BindingFlags.GetProperty, null, table, null).ToString(); ColumAttribute colum = item1 as ColumAttribute; if (colum != null) { columValue.Add(colum.ColumName, value); } } } //拼插入操作字符串 foreach (var item in columValue) { SqlStr.Append(item.Key); SqlStr.Append(","); } SqlStr.Remove(SqlStr.Length - 1, 1); SqlStr.Append(") values('"); foreach (var item in columValue) { SqlStr.Append(item.Value); SqlStr.Append("','"); } SqlStr.Remove(SqlStr.Length - 2, 2); SqlStr.Append(")"); HttpContext.Current.Response.Write(SqlStr.ToString()); } } }
參考博客:《 .NET中反射機制的使用與分析 》《 利用反射動態創建對象 》《C#反射機制》