1.何謂反射?
反射就是在運行的時候發現對象的相關信息。根據這些信息可以動態的執行對象的方法以及獲取對象的屬性所儲存的值。使用.NET Framework編寫的代碼是自動反射的,或者說是自我描述的。之所以可以反射,是通過編譯後產生的元數據來做到的。因此,你可以在你的程序中使用反射來查找託管代碼中的類型(包括類的名稱,方法以及參數)和與其相關的信息這其中包括執行被發現的代碼。你也可以在程序運行的時候使用反射來創建,編譯和運行代碼。
2.反射的實例化
l public static Assembly Load(string);
l public static Assembly LoadFrom(string);
例子:
(1)
(2)
Assembly myAssembly = Assembly.LoadFrom(path);
(3)
(4)
Assembly otherAssembly = dt.GetType().Assembly;
/*先定義一個CLASS,以方便以下例子使用*/
using System.Reflection;
namespace Basics
{
public enum testEnum
{
testValue = 1
}
public class testClass
{
public static void Main()
{
}
}
public class SomeClass
{
public SomeClass()
{}
public SomeClass(int someValue)
{}
public SomeClass(int someValue,int someOtherValue)
{}
public void SomeMethod()
{}
}
public class OtherClass
{
public void OtherMethod()
{}
public static void OtherStaticMethod()
{}
public static void AnotherStaticMethod()
{}
}
public class AnotherClass
{
private int myPrvField1 = 15;
private string myPrvField2 = "Some private field";
public decimal myPubField1 = 1.03m;
}
}
第一部分:找到特定的成員
3.列出程序集中類的名稱
Type[] types = myAssembly.GetTypes();
foreach(Type type in types)
{
if(type.IsClass)
Console.WriteLine(type.Name);
}
4.直接訪問某個類(假如訪問的是SomeClass類中帶一個參數的方法)
ConstructorInfo ci = typeof(SomeClass).GetConstructor(ts);
或者
5.過濾某些特定的成員
System.Type類也提供了一些方法,用於把包含在一個類裏或者其它的類型裏的特定的類型過濾到一個集合中。如GetConstructors方法,GetMethods方法,GetProperties方法和GetEvents方法均允許你以數組的方式返回所有給定的類型或者通過使用過濾條件只返回特定的類型集合。
以下是過濾出是公用和靜態的方法
PS:如果想得到私有類型,則用BindingFlags.NonPublic,但你需要有相應的權限
6.搜索某些特定的成員
AnotherClass ac = new AnotherClass();
MemberInfo[] memInfo = ac.GetType().FindMembers(MemberTypes.Field,BindingFlags.NonPublic | BindingFlags.Instance,null,null);
foreach(MemberInfo m in memInfo)
{
fi = m as FieldInfo;
if(fi != null)
{
Console.WriteLine("{0} of value:{1}",fi.Name,fi.GetValue(ac));
}
}
7.自定義搜索
這個程序定義了一個委託(與委託MemberFilter具有相同簽名的方法)MySearchDelegate,用於定製搜索條件。創建了一個類filterObject,其包含兩個字段,輔助我們自定義搜索條件。程序中調用了FindMembers,指出我們需要所有的屬性類型。當一個屬性類型被發現時,程序將激發MySearchDelegate並且傳給它一個filterCriteria實例對象,這個委託將判斷成員的名稱是否滿足自定義的搜索條件來返回True還是False。
using System.Reflection;
namespace Basics2
{
/**//// <summary>
/// 被反射的類
/// </summary>
public class SomeClass
{
private int m_id;
public int ID
{
get{return this.m_id;}
set{this.m_id = value;}
}
private string m_name;
public string Name
{
get{return this.m_name;}
set{this.m_name = value;}
}
private int m_type;
public int Type
{
get{return this.m_type;}
set{this.m_type = value;}
}
}
/**//// <summary>
/// 自定義的過濾對象類
/// </summary>
public class filterObject
{
public string criterion1 = "Name";
public string criterion2 = "ID";
}
public class Basics
{
/**//// <summary>
/// 自定義的搜索條件,回調的方法
/// </summary>
/// <param name="memberInfo"></param>
/// <param name="filterCriteria"></param>
/// <returns></returns>
public static bool MySearchDelegate(MemberInfo memberInfo,object filterCriteria)
{
if(memberInfo.Name == ((filterObject)filterCriteria).criterion1 || memberInfo.Name == ((filterObject)filterCriteria).criterion2)
return true;
return false;
}
public static void Main()
{
PropertyInfo pi;
//綁定自定義的搜索條件
MemberFilter mf = new MemberFilter(MySearchDelegate);
SomeClass sc = new SomeClass();
//使用FindMembers返回指定的屬性
MemberInfo[] memInfo = sc.GetType().FindMembers(MemberTypes.Property,BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance,mf,new filterObject());
foreach(MemberInfo m in memInfo)
{
pi = m as PropertyInfo;
Console.WriteLine(pi.Name);
}
Console.ReadLine();
}
}
}
第二部分 執行發現的代碼
執行發現的代碼的過程基本上要遵循以下幾個基本的步驟:
l 加載程序集
l 找到你希望使用的類型或者類
l 創建該類型(或者類)的一個實例
l 找到你希望執行的該類型的某個方法
l 得到該方法的參數
l 調用該對象上的方法並且傳遞給它恰當的參數
8.創建發現的代碼的實例
一旦找到你要找的類型,就可以使用System.Activator創建此類型的一個實例。你將會使用Activator類的方法CreateInstance衆多版本中的一個。CreateInstance允許你指定你想要創建的對象,並且可選擇的參數會應用到該對象的構造器上。
(1)在這裏該對象的默認的構造器不用傳遞參數:
object obj = System.Activator.CreateInstance(typeof(SomeClass));
(2)假設你想創建一個特定對象的實例,其構造器是需要傳遞參數的。爲此你需要把這些參數的值作爲一個數組的形式傳遞給CreateInstance。每一個參數的值需要對應該參數的類型,並且數組中參數的值需要與構造器的簽名的順序相一致。
ConstructorInfo ci = typeof(SomeClass).GetConstructor(ts);
ParameterInfo[] pi = ci.GetParameters();
//創建一個數組,與返回的長度一樣
object[] param = new object[pi.Length];
//給數組每個參數賦值
foreach(ParameterInfo p in pi)
{
if(p.ParameterType == typeof(Int32))
param[p.Position] = 100; //賦值
}
//最後可以創建實例化了
object o = System.Activator.CreateInstance(typeof(SomeClass),param);
到此,你已經得到了你的對象(SomeClass)的一個實例(o)。接下來,讓我們瞭解一下,如何調用該對象的方法。在之前,我們查詢構造器的參數並把參數的值傳給構造器,對於方法而言,這個處理過程是一樣的。假設SomaClass類有一個SomeMethod方法,你想調用這個方法。爲了保證例子足夠簡單,假設方法SomeMethod沒有任何參數(參數的處理過程同上)。爲了能夠調用SomeMethod,你需要獲取關於該方法的MethodInfo對象的一個引用。在這裏你可以使用GetMethod或者GetMethods方法在你的類型上搜索。讓我們使用GetMethod,並給其傳遞方法的名稱。
你不僅擁有了SomeClass的一個實例,而且也擁有了你希望調用該對象方法的引用mi,因此你可以使用MethodInfo.Invoke調用你的目標方法了。你需要傳遞包含該方法的對象的實例和該方法需要的一組參數的值。如下所示:
現在已經成功地創建了某個對象的一個實例,找到了該對象的某個方法,並且成功調用了此方法,而這些在設計之初沒有必要知道該對象。
可以很容易的沿着這個例子向外延伸,創建一個類似於測試工具的實用工具。
補充:
補充說明:
1.Assembly myAssembly = Assembly.LoadFrom("TestClass.dll");
Type[] myType = myAssembly.GetTypes();
以上myType中的每個元素實際上是指dll中的每個CLASS類.如果要控制第一個類則用myType[0]來表示.
2. MethodInfo[] mi = myType[0].GetMethods();
foreach(MethodInfo m in mi)
{
// this.textBox1.Text += m.Name + "/r/n";
}
以上代碼獲得DLL第一個類中的所有方法
3. PropertyInfo[] pi = myType[0].GetProperties();
foreach(PropertyInfo p in pi)
{
// this.textBox1.Text += p.Name + "/r/n";
}
以上代碼獲得DLL中第一個類的所有屬性
4. 其實用MemberInfo[] 可以獲得以上所有屬性及方法
5. 執行DLL中的方法前需要實例化構造方法獲得入口實例.
Type[] ts = {typeof(String),typeof(Int32)};//構造方法的參數,如果沒有可以省略
ConstructorInfo ci = myType[0].GetConstructor(ts);//在重載中找到相應的構造函數
ParameterInfo[] pi = ci.GetParameters();//取得構造函數的參數信息
object[] paras = new object[pi.Length];
paras[0] = "ZZY";//給參數賦值,假設有兩個參數,一個爲String,一個爲Int
paras[1] = 10;
object o = System.Activator.CreateInstance(myType[0],paras);//創建實例
// this.textBox1.Text += o.ToString() + "/r/n";
MethodInfo mi = myType[0].GetMethod("WriteFile");//找到要執行的方法
ParameterInfo[] pi1 = mi.GetParameters();//獲得方法的參數信息
object[] paras1 = new object[pi1.Length];
paras1[0] = "test.txt";//給參數賦值,假設有兩個參數都爲String
paras1[1] = "Pls have your test";
mi.Invoke(o,paras1); //執行方法