今天就平常用到的非常多的反射這個技術來做一個總結,當然關於反射需要講解的東西實在是太多的內容,在一片文章中想要講解清楚是非常難的,本篇博客也是就自己本人對這些內容學習后的一個總結,當然包括看書和自己寫過的一些代碼中抽取的一些示例,而且本文也僅限於此時對於這個知識點的理解,希望通過以后的逐步學習能夠不斷加深對這個知識點的理解。
首先來看看對於反射的基礎知識點。
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,
}
