今天就平常用到的非常多的反射這個技術來做一個總結,當然關於反射需要講解的東西實在是太多的內容,在一片文章中想要講解清楚是非常難的,本篇博客也是就自己本人對這些內容學習后的一個總結,當然包括看書和自己寫過的一些代碼中抽取的一些示例,而且本文也僅限於此時對於這個知識點的理解,希望通過以后的逐步學習能夠不斷加深對這個知識點的理解。
首先來看看對於反射的基礎知識點。
1 定義:首先看看MSDN怎樣對它進行解釋吧
反射提供了封裝程序集、模塊和類型的對象(Type 類型)。可以使用反射動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型並調用其方法或訪問其字段和屬性。如果代碼中使用了屬性,可以利用反射對它們進行訪問。
2 反射有什么作用?
A、將類型綁定到現有對象,或從現有對象中獲取類型信息,這些信息包括(Assembly MemberInfo EventInfo FieldInfo MethodBase ConstructorInfo MethodInfo PropertyInfo 等等 )另外可以使用反射動態地創建類型的實例,
例如:
1、System.Activator 的CreateInstance方法。該方法返回新對象的引用。
2、System.Activator 的CreateInstanceFrom 與上一個方法類似,不過需要指定類型及其程序集
3、System.Appdomain 的方法:CreateInstance,CreateInstanceAndUnwrap,CreateInstranceFrom和CreateInstraceFromAndUnwrap
4、System.Type的InvokeMember 實例方法:這個方法返回一個與傳入參數相符的構造函數,並構造該類型。
5、System.Reflection.Constructinfo 的Invoke實例方法
下面通過一段代碼來了解這5種創建實例的方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Diagnostics; using OptimizeReflection; using System.Collections; using System.Web; namespace TestOptimizeReflection { public class OrderInfo { public int OrderID { get; set; } public DateTime OrderDate { get; set; } public decimal SumMoney { get; set; } public string Comment { get; set; } public bool Finished { get; set; } public int Add(int a, int b) { return a + b; } } class Program { static void Main() { TestNewInstance(); } static void TestNewInstance() { //常規方法創建對象 OrderInfo testObj = new OrderInfo(); #region 常規反射創建 //利用反射來動態創建對象 Type instanceType = typeof(OrderInfo); OrderInfo orderInfo = (OrderInfo)Activator.CreateInstance(instanceType); #endregion #region Activator.CreateInstanceFrom 創建 //注意Assembly.GetEntryAssembly().CodeBase表示當前執行的exe所在的路徑,instanceType.FullName表示當前OrderInfo的類型名稱TestOptimizeReflection.OrderInfo System.Runtime.Remoting.ObjectHandle oh= Activator.CreateInstanceFrom(Assembly.GetEntryAssembly().CodeBase, instanceType.FullName); //返回被包裝的對象 OrderInfo orderInfoEx = (OrderInfo)oh.Unwrap(); #endregion #region System.AppDomain.CurrentDomain實例創建對象 System.Runtime.Remoting.ObjectHandle ohEx = System.AppDomain.CurrentDomain.CreateInstance(Assembly.GetEntryAssembly().FullName, instanceType.FullName); OrderInfo orderInfoExEx = (OrderInfo)ohEx.Unwrap(); //合並上面的兩步 OrderInfo orderInfoExExEx = (OrderInfo)System.AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, instanceType.FullName); #endregion #region InvokeMember方法創建實例 OrderInfo invokeMemberOrderInfo = (OrderInfo)instanceType.InvokeMember(null, BindingFlags.Public| BindingFlags.Instance | BindingFlags.Static|BindingFlags.CreateInstance, System.Type.DefaultBinder, null, null); #endregion #region 調用構造器創建 //調用無參數的默認構造函數 ConstructorInfo ci = instanceType.GetConstructor(new Type[] { }); OrderInfo ciOrderInfo = (OrderInfo)ci.Invoke(null); #endregion } } }
B、應用程序需要在運行時從某個特定的程序集中載入一個特定的類型,以便實現某個任務時可以用到反射。
這個該怎樣去理解呢?這個可以用插件系統中的同類思想去解釋,在構建插件系統的時候,我們有時候需要主程序去動態地調用插件,可能應用程序只有在執行某一操作的時候才能夠去調用相關的DLL,這個時候我們就可以通過反射這種方式來動態調用dll中分特定方法或者類型,這個也是經常使用到的。
C、反射主要應用與類庫,這些類庫需要知道一個類型的定義,以便提供更多的功能。
D、反射能夠調用一些私有方法和字段等。
這個需要重點去講述,我們知道常規的實例方法由於受到作用域的影響,很多時候當方法設置為Private或者是其它的限制訪問的關鍵字時就無能為力了,這個也是完全能夠體現類的封裝性,但是通過反射就能夠完全繞開這些限制,下面舉出一些對私有變量或字段、方法的一些訪問方式,能夠直接進行訪問,這樣還是非常方便的,具體請參考下面的代碼。
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Dvap.Infrastructure.Utils { public static class ReflectionUtil { /// <summary> /// 得到私有字段的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="instance"></param> /// <param name="fieldname"></param> /// <returns></returns> public static T GetPrivateField<T>(this object instance, string fieldname) { BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic; Type type = instance.GetType(); FieldInfo field = type.GetField(fieldname, flag); return (T)field.GetValue(instance); } /// <summary> /// 設置私有成員的值 /// </summary> /// <param name="instance"></param> /// <param name="fieldname"></param> /// <param name="value"></param> public static void SetPrivateField(this object instance, string fieldname, object value) { BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic; Type type = instance.GetType(); FieldInfo field = type.GetField(fieldname, flag); field.SetValue(instance, value); } /// <summary> /// 得到私有屬性的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="instance"></param> /// <param name="propertyname"></param> /// <returns></returns> public static T GetPrivateProperty<T>(this object instance, string propertyname) { BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic; Type type = instance.GetType(); PropertyInfo field = type.GetProperty(propertyname, flag); return (T)field.GetValue(instance, null); } /// <summary> /// 設置私有屬性的值 /// </summary> /// <param name="instance"></param> /// <param name="propertyname"></param> /// <param name="value"></param> public static void SetPrivateProperty(this object instance, string propertyname, object value) { BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic; Type type = instance.GetType(); PropertyInfo field = type.GetProperty(propertyname, flag); field.SetValue(instance, value, null); } /// <summary> /// 調用私有方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="instance"></param> /// <param name="name"></param> /// <param name="param"></param> /// <returns></returns> public static T CallPrivateMethod<T>(this object instance, string name, params object[] param) { BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic; Type type = instance.GetType(); MethodInfo method = type.GetMethod(name, flag); return (T)method.Invoke(instance, param); } } }
E 還可以通過反射來獲取屬性或者類上的特性
有時候我們需要在類或者屬性上面添加自定義的特性,並且通過反射能夠獲取到這些特性,那么這個時候我們就可以使用反射來達到目的了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Diagnostics; using OptimizeReflection; using System.Collections; using System.Web; namespace TestOptimizeReflection { /// <summary> /// 自定義特性 屬性或者類可用 支持繼承 /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true)] public class EnitityMappingAttribute : Attribute { private string tableName; /// <summary> /// 實體實際對應的表名 /// </summary> public string TableName { get { return tableName; } set { tableName = value; } } private string columnName; /// <summary> /// 中文列名 /// </summary> public string ColumnName { get { return columnName; } set { columnName = value; } } } /// <summary> /// 會員 ,實際的表名叫MemberInfo,並不是和實體名一致 /// </summary> [EnitityMapping(TableName = "MemberInfo")] public class Member { private int id; [EnitityMapping(ColumnName = "關鍵字")] public int Id { get { return id; } set { id = value; } } private string userName; [EnitityMapping(ColumnName = "會員注冊名")] public string UserName { get { return userName; } set { userName = value; } } private string realName; [EnitityMapping(ColumnName = "會員真實名")] public string RealName { get { return realName; } set { realName = value; } } private bool isActive; /// <summary> /// 是否活躍 沒有附加自定義屬性 /// </summary> public bool IsActive { get { return isActive; } set { isActive = value; } } } class Program { /// <summary> /// 通過反射取自定義屬性 /// </summary> /// <typeparam name="T"></typeparam> private static void DisplaySelfAttribute<T>() where T : class, new() { string tableName = string.Empty; List<string> listColumnName = new List<string>(); Type objType = typeof(T); //取屬性上的自定義特性 foreach (PropertyInfo propInfo in objType.GetProperties()) { object[] objAttrs = propInfo.GetCustomAttributes(typeof(EnitityMappingAttribute), true); if (objAttrs.Length > 0) { EnitityMappingAttribute attr = objAttrs[0] as EnitityMappingAttribute; if (attr != null) { listColumnName.Add(attr.ColumnName); //列名 } } } //取類上的自定義特性 object[] objs = objType.GetCustomAttributes(typeof(EnitityMappingAttribute), true); foreach (object obj in objs) { EnitityMappingAttribute attr = obj as EnitityMappingAttribute; if (attr != null) { tableName = attr.TableName;//表名只有獲取一次 break; } } if (string.IsNullOrEmpty(tableName)) { tableName = objType.Name; } Console.WriteLine(string.Format("The TableName of the entity is:{0} ", tableName)); if (listColumnName.Count > 0) { Console.WriteLine("The Columns of the table are as follows:"); foreach (string item in listColumnName) { Console.WriteLine(item); } } } static void Main() { DisplaySelfAttribute<Member>(); //顯示結果 Console.ReadLine(); } } }
最終呈現的效果:
這里簡單說一下AttributeUsage的一些用法,AttributeUsage 有三個 屬性 ,分別是
public bool AllowMultiple { get; set; } 作用:是否能在一個目標身上多次使用
public bool Inherited { get; set; } 作用 :特性是否能繼承到子類身上
public AttributeTargets ValidOn { get; } 作用:設置特性的可使用范圍
這里面就是要解釋一下AttributeTargets 的特性使用范圍,這個主要是用在繼承自Attribute的子類里面,在本例中我們自定義的特性主要用於標識類和屬性
public enum AttributeTargets { // 摘要: // 可以對程序集應用特性。 Assembly = 1, // // 摘要: // 可以對模塊應用特性。 Module = 2, // // 摘要: // 可以對類應用特性。 Class = 4, // // 摘要: // 可以對結構應用特性,即值類型。 Struct = 8, // // 摘要: // 可以對枚舉應用特性。 Enum = 16, // // 摘要: // 可以對構造函數應用特性。 Constructor = 32, // // 摘要: // 可以對方法應用特性。 Method = 64, // // 摘要: // 可以對屬性應用特性。 Property = 128, // // 摘要: // 可以對字段應用特性。 Field = 256, // // 摘要: // 可以對事件應用特性。 Event = 512, // // 摘要: // 可以對接口應用特性。 Interface = 1024, // // 摘要: // 可以對參數應用特性。 Parameter = 2048, // // 摘要: // 可以對委托應用特性。 Delegate = 4096, // // 摘要: // 可以對返回值應用特性。 ReturnValue = 8192, // // 摘要: // 可以對泛型參數應用特性。 GenericParameter = 16384, // // 摘要: // 可以對任何應用程序元素應用特性。 All = 32767, }