前言:反射在C#中雖然不常用(如果不需要動態加載xx.dll),但是有時候卻是設計某個程序或者完成某類功能比較好用的技術。比如:一個支持動態擴展的程序,這樣就需要動態加載dll,動態創建加載dll的程序集,最終完成操作。
一、加載程序集
對於程序集的加載一般會使用兩個方法來進行:
1.Assembly.Load(string assemblyName),AssemblyName為程序集的長格式名稱。
Assembly SampleAssembly = Assembly.Load("SampleAssembly, Version=1.0.2004.0, Culture=neutral, PublicKeyToken=8744b20f8da049e3"); foreach (Type oType in SampleAssembly.GetTypes()) { Console.WriteLine(oType.Name); }
2.Assembly.Load(AssemblyName assemblyName),assemblyName為完整描述程序集的唯一標識,通過Assembly.GetName()方法可得到程序集的唯一標識。
3.Assembly.LoadFile(string path),加載指定路徑上的程序集文件的內容。
4.Assembly.LoadFrom(string assemblyFile),已知程序集的文件名或路徑,加載程序集。
Assembly SampleAssembly; SampleAssembly = Assembly.LoadFile(@"C:\Users\Admin\Documents\visual studio 2013\Projects\ConsoleApplication3\Sample\bin\Debug\Sample.dll"); MethodInfo Method = SampleAssembly.GetTypes()[0].GetMethod("DoWork"); ParameterInfo[] Params = Method.GetParameters(); foreach (ParameterInfo Param in Params) { Console.WriteLine("Param=" + Param.Name.ToString()); Console.WriteLine(" Type=" + Param.ParameterType.ToString()); Console.WriteLine(" Position=" + Param.Position.ToString()); Console.WriteLine(" Optional=" + Param.IsOptional.ToString()); }
LoadFile和LoadFrom方法在大部分情況下是一樣的結果,但是還是有區別的,如下:
1、Assembly.LoadFile只載入相應的dll文件,比如Assembly.LoadFile("a.dll"),則載入a.dll,假如a.dll中引用了b.dll的話,b.dll並不會被載入。 Assembly.LoadFrom則不一樣,它會載入dll文件及其引用的其他dll,比如上面的例子,b.dll也會被載入。 2、用Assembly.LoadFrom載入一個Assembly時,會先檢查前面是否已經載入過相同名字的Assembly,比如a.dll有兩個版本(版本1在目錄1下,版本2放在目錄2下),程序一開始時載入了版本1,當使用Assembly.LoadFrom("2\\a.dll")載入版本2時,不能載入,而是返回版本1。Assembly.LoadFile的話則不會做這樣的檢查。
除了上述使用外,還可以從一個url中加載一個dll文件:
SampleAssembly = Assembly.LoadFrom("http://http://www.cnblogs.com/ListenFly/Sample.dll");
雖然此地址不存在,但是此方式是可行的。
5.僅僅加載程序集
如果構建的工具只是通過反射來分析程序元數據,並希望確保程序集中的任何代碼都不會執行,那么加載程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad。
ReflectionOnlyLoadFrom方法將加載由路徑指定的文件;和Load不同的是,ReflectionOnlyLoad方法不會應用版本控制策略,所以你指定的是哪個版本,獲得的就是哪個版本。
用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加載程序集時,CLR禁止程序集中的任何代碼執行;試圖執行由這兩個方法加載的程序集中的代碼,會導致CLR拋出異常。
6.將dll和exe打包在一起,並加載
有時候我們只會發布一個exe,甚至說不用安裝直接運行就可以的軟件,這時候就可以將要引用的dll添加到項目中,並且設置文件的屬性(Properties)中的Build Action(生成操作)設置為嵌入資源(Embed Resource)。在運行時,CLR會找不到依賴的DLL程序集。為了解決這個問題,當應用程序初始化時,向AppDomain的ResolveAssembly事件登記一個回調方法,如下:
AppDomain.CurrentDomain.AssemblyResolve +=(sender, e) => { string resourceName = "AssemblyLoadingAndReliection." + new AssemblyName(e.Name) + ".dll"; using (var stream=Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { Byte[] assemblyData=new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } };
現在,一個線程首次調用一個方法時,如果發現該方法引用了依賴DLL文件中的一個類型,就會引發一個AssemblyResolve事件,而上述回調代碼會找到所需的嵌入DLL資源,並調用Assembly的Load方法的一個重載版本(傳遞一個Byte[]實參),從而加載所需的資源。
二、關於反射的性能
雖然反射相當的強大,允許在運行時發現並使用編譯時還不了解的類型及其成員。但是有兩個缺點:
1.反射會造成編譯時無法保證類型安全性,由於反射要嚴重依賴字符串,所以會喪失編譯時的類型安全性。
2.反射速度慢。使用反射時,類型及其成員的名稱在編譯時未知;要用字符串名稱標識每個類型及其成員,以便在運行時發現他們。也就是說,需要掃描程序集的元數據,並且要不斷地執行字符串。
基於上述所有原因,最好避免利用發射來訪問字段或者調用方法/屬性。如果要寫一個應用程序來動態發現和構造類型實例,應采取以下兩種技術之一。
1.讓類型從一個編譯時已知的基類型派生。在運行時,構造派生類型的一個實例,將對它的引用放到基類型的一個變量中,再調用基類型定義的虛方法。
2.讓類型實現一個編譯時已知的接口。在運行時,構造派生類型的一個實例,將對它的引用放到接口的一個變量中,再調用接口定義的方法。
方法1可以用來控制類的版本,因為隨時都能向基類添加一個成員,派生類則直接繼承;方法2則是從功能上選擇一個比較好的對象來操作。
三、反射程序集中的類型
1.發現程序集中的類型
反射經常用於判斷一個程序集中定義了哪些類型。最常用的方法是Assembly的GetExportedTypes。
string assemblyName = @"System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; Assembly assembly = Assembly.Load(assemblyName); foreach (var t in assembly.GetExportedTypes()) { Console.WriteLine(t.FullName); }
2.GetType方法
Object.GetType方法返回當前類型的RuntimeType對象的一個引用。此方法可以用來判斷當前類型是否是某種類型:
if(o.GetType() == typeof(MyType))
除了上述方式 is 關鍵字也可以用來判斷對象是否是某種類型。
3.構造類型的實例
- System.Activator的CreateInstance方法,Activator提供了CreateInstance靜態方法,此方法可以傳遞一個Type對象,也可以傳遞標識了想要創建的類型的一個string。
string的重載,第一個參數為程序集名稱,第二個參數為類型名稱(完全限定名,即namespace+typeName)。
ObjectHandle handle = Activator.CreateInstance("PersonInfo", "Person"); Person p = (Person) handle.Unwrap();
type參數重載,調用指定類型的默認構造函數。
objct o= Activator.CreateInstance(typeof(object));
上述方法返回的不是新對象的引用,而是一個System.Runtime.Remoting.ObjectHanlde對象(派生自System.MarshalByRefObject)。ObjectHandle類型允許將一個AppDomain中創建的對象傳至其他AppDomain,期間不強迫對象具體化。所以要具體化對象時,請調用ObjectHandle的Unwrap方法。
- System.Activator的CreateInstanceFrom方法,Activator提供了一組靜態的CreateInstanceFrom方法。這些方法與CreateInstance行為相似,只是必須通過字符串來指定類型及其程序集。同樣要使用ObjectHandle的Unwrap方法進行具體化對象。
- System.AppDomain的方法 AppDomain提供了4個用於構造類型實例的實例方法:CreateInstance、CreateInstanceAndUnwrap、CreateInstanceFrom以及CreateInstanceFromAndUnwrap。這些方法的行為和Activator的行為類似,只是它們都是實例方法,允許指定在哪個AppDomain中構造對象。另外,帶Unwrap后綴的方法還能簡化操作。
- System.Type的InvokeMember實例方法,可以使用一個Type對象引用來調用InvokeMember方法。System.Reflection.ConstructorInfo的Invoke實例方法,使用一個Type對象引用,可以綁定到一個特定的構造器,並獲取對構造器的ConstructorInfo對象的一個引用。然后可以利用對這個ConstructorInfo對象的引用調用它的Invoke方法。
構造泛型:
Type openType = typeof(Dictionary<,>); Type closedType = openType.MakeGenericType(typeof(string), typeof(int)); object o = Activator.CreateInstance(closedType); Console.WriteLine(o.GetType());
4.設計一個支持加載項的應用程序
一個類庫Host,包含自己的接口
namespace Host { public interface IMyInstance { string DoWork(int parameter); } }
實現接口的兩個類(在類庫Sample中定義):
public class MyClass1 : IMyInstance { public string DoWork(int parameter) { return "Class1:" + parameter.ToString(); } }
public class MyClass2:IMyInstance { public string DoWork(int parameter) { return "Class2:" + parameter.ToString(); } }
加載程序所引用的dll文件,並加載所有dll的程序集,最終找到實現自IMyInstance接口的類:
static void Main(string[] args) { //獲取當前運行程序的路徑 var hostDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); //加載路徑中的所有dll string[] hostAssemblies = Directory.GetFiles(hostDir, "*.dll"); List<Type> hostTypes = new List<Type>(); foreach (var file in hostAssemblies) { //將dll文件加載到程序集中 Assembly hostAssembly = Assembly.LoadFrom(file); foreach (var type in hostAssembly.GetExportedTypes()) { //確定type為類並且繼承自(實現)IMyInstance if (type.IsClass && typeof(IMyInstance).IsAssignableFrom(type)) hostTypes.Add(type); } } foreach (var type in hostTypes) { IMyInstance instance = (IMyInstance)Activator.CreateInstance(type); Console.WriteLine(instance.DoWork(10)); } Console.Read(); }
當前假設,Main函數所在的程序,添加了Host和Sample類庫,所以在Debug中有兩個dll文件。
四、使用反射發現類型的成員
1.發現類型成員
字段、構造器、方法、屬性、事件和嵌套類型都可以被定義為一個類型的成員。FCL包含一個名為System.Reflection.MemberInfo類型。此類型為抽象類,封裝一組所有類型成員的通用屬性。從MemberInfo派生的是一組類,每個類都封裝了與一個特定類型成員相關的更多屬性。
static void Main(string[] args) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) { WritLine(0, "Assembly:{0}", assembly); foreach (var type in assembly.GetExportedTypes()) { BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; foreach (var memberInfo in type.GetMembers(flags)) { var typeName = string.Empty; if (memberInfo is Type) typeName = "Nested Type"; else if (memberInfo is FieldInfo) typeName = "FieldInfo"; else if (memberInfo is MethodInfo) typeName = "MethodInfo"; else if (memberInfo is ConstructorInfo) typeName = "ConstructorInfo"; else if (memberInfo is PropertyInfo) typeName = "PropertyInfo"; else if (memberInfo is EventInfo) typeName = "EventInfo"; WritLine(2, "{0}: {1}", typeName, memberInfo); } } } Console.Read(); } static void WritLine(int indent, string format, params object[] args) { Console.WriteLine(new string(' ', 3 * indent) + format, args); }
上述為加載當前AppDomain中的所有程序集、以及其類型和類型的成員,並輸出。
2.BindingFlags篩選返回的成員種類
可以使用Type的GetMembers、GetNextedTypes、GetFields、GetConstructors、GetMethods、GetProperties、GetEvents方法查詢一個類型的成員。使用上述方法時,可以傳遞BindingFlags枚舉類型的一個實例。這個類型標識了一組通過邏輯OR運算合並到一起的位標識(通過在枚舉添加 [Flags]實現),默認設置是BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static(如果指定了Public或者NonPublic,那么必須同時指定Instance | Static,否則將不返回成員)。
3.發現類型的接口
通過Type類型的FindInterfaces、GetInterface或者GetInterfaces方法。所有這些方法都返回代表接口的Type對象。為了獲得一個特定接口的MethodInfo對象,可以調用Type的GetInterfaceMap實例方法(傳遞接口類型作為參數)。該方法返回System.Reflection.InterfaceMapping的一個實例。
InterfaceMapping類型的公共字段:
TargetType Type類型 用於調用GetInterfaceMapping的類型
InterfaceType Type類型 傳給GetInterfaceMapping的接口類型
InterfaceMethods MethodInfo[]類型 一個數組,每個元素表示接口中的一個方法的信息
TargetMethods MethodInfo[]類型 一個數組,每個元素表示由當前類型實現自接口的一個方法
InterfaceMethods和TargetMethods數組是相互對應的。也就是說InterfaceMethods[0]表示的是接口的MethodInfo,那么TargetMethods[0]表示的是類型定義的方法(實現自接口)。
下面一個發現類型實現的接口的例子:
interface IBookRetailer:IDisposable { void Purchase(); void ApplyDiscount(); }
interface IMusicRetailer { void Pruchase(); }
public class MyRetailer:IBookRetailer,IMusicRetailer { /// <summary> /// MyRetailer類自己的Purchase方法 /// </summary> public void Purchase() { } /// <summary> /// IMusicRetailer的Pruchase方法 /// </summary> void IMusicRetailer.Pruchase() { } /// <summary> /// IBookRetailer的Purchase方法 /// </summary> void IBookRetailer.Purchase() { } public void ApplyDiscount() { } public void Dispose() { } }
上述代碼定義了兩個接口一個類,然后讓這個類實現兩個接口,這點都沒有任何特殊。特殊就在於,兩個接口擁有同樣的一個方法就是Pruchase,同時類本身也有一個這樣名稱的方法,防止出現方法的沖突,這里使用了顯示實現接口,即方法的前綴為具體的接口,這樣雖然在類中有3個Pruchase方法,但是由於是指定了其所屬接口,所以是沒有問題的。
static void Main(string[] args) { Type type = typeof(MyRetailer); Type[] interfaces = type.FindInterfaces(TypeFilter, typeof(Program).Assembly); Console.WriteLine("MyRetailer implements the following " + " instances (defined in this assemly) :"); foreach (var interfaceType in interfaces) { Console.WriteLine("\nInterface:" + interfaceType); //獲取映射接口方法的類型的方法 InterfaceMapping mapping = type.GetInterfaceMap(interfaceType); for (int i = 0; i < mapping.InterfaceMethods.Length; i++) { Console.WriteLine(" {0} is Implemented by {1}", mapping.InterfaceMethods[i], mapping.TargetMethods[i]); } } Console.Read(); } /// <summary> /// 如果類型匹配篩選器條件,就返回true /// </summary> /// <param name="t"></param> /// <param name="filterCriteria"></param> /// <returns></returns> private static bool TypeFilter(Type t, object filterCriteria) { //如果接口和filterCriteria標識的程序集中定義的,就返回true return t.Assembly == filterCriteria; }
上述代碼用於查找MyRetailer的所有接口,FindInterfaces有兩個參數,第一個為返回值為bool類型的過濾方法,第二個參數為方法的參數。我們的TypeFilter即為第一個方法,有兩個參數,一個為Type(自動判斷,根據當前調用此方法的類型的接口類型),第二個就是參數了。在方法中判斷,只有接口的程序集和指定的程序集一直才認為是true。
通過FindInterfaces得到Type數組,然后使用Type.GetInterfaceMap得到接口和類型的映射,最后根據InterfaceMethods和TargetMethods輸出實現的信息。
P.S. 是不是發現輸出的信息中並沒有IDisposable接口的信息,那是因為在TypeFilter方法中我們過濾了和指定的程序集不一致的接口,而IDisposable當然不和Exe在同一個程序集,所以就沒有查找到。
4.調用類型的成員
通過反射得到類型的成員是沒有太多意義的,我們更多地是去操作成員。比如,可以調用FieldInfo進行設置或獲取字段的值;調用ConstructorInfo,訪問構造函數,並獲得實例;也可以調用一個MethodInfo,進行執行方法,如果有返回值則可以得到;PropertyInfo可以調用屬性的get和se方法;EventInfo可以添加或者刪除一個事件。
上述的所有操作,我們都可以使用Type.InvokeMember方法進行實現:
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture);
name:成員名稱;invokeAttr:如何查找成員;binder:如何匹配成員和實參;target:要調用其成員的對象;args:要傳給方法的實參;culture:某些綁定器使用的語言文化。
binder表示執行InvokeMember方法時選擇成員時候使用的規則,從候選者列表中選擇一個成員,並執行實參類型到形參類型的類型轉換,如果傳遞null則默認使用DefauleBinder,當然我們可以自己定義新的Binder(詳見MSDNBinder類介紹)。
下表列出了默認聯編程序支持的轉換。
| 源類型 |
目標類型 |
|---|---|
| 任何類型 |
它的基類型。 |
| 任何類型 |
它實現的接口。 |
| Char |
Unt16、UInt32、Int32、UInt64、Int64、Single、Double |
| Byte |
Char、Unt16、Int16、UInt32、Int32、UInt64、Int64、Single、Double |
| SByte |
Int16、Int32、Int64、Single、Double |
| UInt16 |
UInt32、Int32、UInt64、Int64、Single、Double |
| Int16 |
Int32、Int64、Single、Double |
| UInt32 |
UInt64、Int64、Single、Double |
| Int32 |
Int64、Single、Double |
| UInt64 |
Single、Double |
| Int64 |
Single、Double |
| Single |
Double |
5.一次綁定,多次調用
使用Type的InvokeMember方法可以訪問一個類型的所有成員。但是,應該注意,每次調用InvokeMember方法時,它都必須綁定到一個特定的成員,然后才能調用它。如果每次調用一個成員都進行這個操作,那么性能肯定是會受到影響。所以,如果打算頻繁訪問一個成員,最好是一次綁定,多次調用。
綁定成員后,如果調用成員:
FieldInfo 調用GetValue獲取字段的值;調用SetValue設置字段的值。
ConstructorInfo 調用Invoke構造類型的一個實例,並調用構造函數。
MethodInfo 調用Invoke調用類型的一個方法。
PropertyInfo 調用GetValue調用屬性的get訪問器方法;調用SetValue調用屬性的set構造器方法。
EventInfo 調用AddEventHandler調用事件的add訪問器方法;調用RemoveEventHanlder調用事件的remove訪問器方法。
此外,EventInfo還提供了GetAddMethod和GetRemoveMethod方法,都返回一個MethodInfo,這個MethodInfo對應事件添加或刪除委托的方法。要添加或刪除一個委托,可調用這些MethodInfo對象,也可調用EventInfo類型提供的AddEventhandler和RemoveEventHanlder方法。
下面例子演示了使用反射來范文類型成員的各種方式:
SomeType類:
class SomeType { private int m_someField; public SomeType(ref int x) { x *= 2; } public override string ToString() { return m_someField.ToString(); } public int SomeProp { get { return m_someField; } set { if (value < 1) throw new ArgumentOutOfRangeException("值不在范圍之內"); m_someField = value; } } public event EventHandler SomeEvent; private void NoCompilerWarnings() { SomeEvent.ToString(); } }
此類有多個成員,一個私有變量,一個公共屬性,一個公共構造器(以引用方式傳遞的int類型參數),一個公用方法,以及一個公共事件。
一共使用四種方式來訪問SomeType的成員:
- UseInvokeMemberToBindAndInvokeTheMember方法演示了利用Type的InvokeMember來綁定並調用一個成員。
- BindToMemberThenInvokeTheMember方法演示了如何綁定到一個成員,並在以后調用它。如果打算在不同對象上多次調用同一個成員,那么這個方法可以提高性能。
- BindToMemberCreateDelegateToMemberThenInvokeTheMember方法演示了如何綁定到一個對象或成員,然后創建一個委托來引用該對象或成員。通過委托來調用的速度非常快。如果想在相同的對象上多次調用相同的成員,那么此技術比上一個技術速度還要快。
- UseDynamicToBindAndInvokeTheMember方法演示了如何使用C#的dynamic(.Net 4.0新特性)基元類型來簡化訪問成員時的語法。另外,如果打算在相同類型的不同對象上調用相同的成員,此技術性能還不錯,因為針對每個類型,綁定都只會發生一次,而且可以緩存起來,以后多次調用時速度會很快。還可以使用此技術調用不用類型的對象的成員。
private static void UseInvokeMemberToBindAndInvokeTheMember(Type t) { Console.WriteLine("UseInvokeMemberToBindAndInvokeTheMember"); //構造一個Type的實例 object[] args = new object[] { 20 }; Console.WriteLine("x before constructor called:" + args[0]); object obj = t.InvokeMember(null, flags | BindingFlags.CreateInstance, null, null, args); Console.WriteLine("Type: " + obj.GetType().ToString()); Console.WriteLine("x after constructor returns:" + args[0]); //讀寫一個字段 t.InvokeMember("m_someField", flags | BindingFlags.SetField, null, obj, new object[] { 25 }); int value = Convert.ToInt16(t.InvokeMember("m_someField", flags | BindingFlags.GetField, null, obj, null)); Console.WriteLine("someField:" + value); //調用一個方法 string str = (string)t.InvokeMember("ToString", flags | BindingFlags.InvokeMethod, null, obj, null); Console.WriteLine("ToString:" + str); //讀寫一個屬性 try { t.InvokeMember("SomeProp", flags | BindingFlags.SetProperty, null, obj, new object[] { 5 }); } catch (TargetInvocationException) { Console.WriteLine("Set Property Catch!"); } t.InvokeMember("SomeProp", flags | BindingFlags.SetProperty, null, obj, new object[] { 30 }); value = Convert.ToInt16(t.InvokeMember("SomeProp", flags | BindingFlags.GetProperty, null, obj, null)); Console.WriteLine("SomeProp:" + value); //調用事件add/remove方法,為事件添加和刪除一個委托 EventHandler handler = new EventHandler(EventCallback); t.InvokeMember("add_SomeEvent", flags | BindingFlags.InvokeMethod, null, obj, new object[] { handler }); t.InvokeMember("remove_SomeEvent", flags | BindingFlags.InvokeMethod, null, obj, new object[] { handler }); } private static void BindToMemberThenInvokeTheMember(Type t) { //構造一個實例,之所以GetConstructor的參數為MakeByRefType,那是因為SomeType //的構造函數的參數為ref引用傳遞 ConstructorInfo constructorInfo = t.GetConstructor(new Type[] { typeof(Int32).MakeByRefType() }); object[] args = new object[] { 20 }; Console.WriteLine("x before constructor called: " + args[0]); object obj = constructorInfo.Invoke(args); Console.WriteLine("Type:" + obj.GetType().ToString()); Console.WriteLine("x after constructor returns:" + args[0]); //讀寫一個字段 FieldInfo fieldInfo = obj.GetType().GetField("m_someField",flags); fieldInfo.SetValue(obj, 25); Console.WriteLine("someField:" + fieldInfo.GetValue(obj)); //調用一個方法 MethodInfo methodInfo = obj.GetType().GetMethod("ToString", flags); string str = (string)methodInfo.Invoke(obj, null); Console.WriteLine("ToString:" + str); //讀寫一個屬性 PropertyInfo propertyInfo = obj.GetType().GetProperty("SomeProp", typeof(Int32)); try { propertyInfo.SetValue(obj, 5, null); } catch (Exception) { Console.WriteLine("Property set catch!"); } propertyInfo.SetValue(obj, 30, null); Console.WriteLine("SomeProp:" + propertyInfo.GetValue(obj, null)); //為事件添加和刪除一個委托 EventInfo eventInfo = obj.GetType().GetEvent("SomeEvent", flags); EventHandler handler = new EventHandler(EventCallback); eventInfo.AddEventHandler(obj, handler); eventInfo.RemoveEventHandler(obj, handler); } private static void BindToMemberCreateDelegateToMemberThenInvokeTheMember(Type t) { Console.WriteLine("BindToMemberCreateDelegateToMemberThenInvokeTheMember"); //構造一個實例(不能創建對構造函數的委托) object[] args = new object[] { 20 }; Console.WriteLine("x before constructor called:" + args[0]); object obj = Activator.CreateInstance(t, args); Console.WriteLine("Type:" + obj.GetType().ToString()); Console.WriteLine("x after constructor returns:" + args[0]); //調用一個方法 MethodInfo methodInfo = obj.GetType().GetMethod("ToString", flags); var toString = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), obj, methodInfo); string str = toString(); Console.WriteLine("ToString:" + str); //讀寫一個屬性 PropertyInfo propertyInfo = obj.GetType().GetProperty("SomeProp", typeof(Int32)); var setSomeProp = (Action<Int32>)Delegate.CreateDelegate(typeof(Action<Int32>), obj, propertyInfo.GetSetMethod()); try { setSomeProp(5); } catch (Exception) { Console.WriteLine("Property set catch!"); } setSomeProp(60); var getSomeProp = (Func<Int32>)Delegate.CreateDelegate(typeof(Func<Int32>), obj, propertyInfo.GetGetMethod()); Console.WriteLine("SomeProp:" + getSomeProp()); //從事件中添加和刪除一個委托 EventInfo eventInfo = obj.GetType().GetEvent("SomeEvent", flags); var addSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, eventInfo.GetAddMethod()); addSomeEvent(EventCallback); var removeSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, eventInfo.GetRemoveMethod()); removeSomeEvent(EventCallback); } private static void UseDynamicToBindAndInvokeTheMember(Type t) { Console.WriteLine("UseDynamicToBindAndInvokeTheMember"); //構造一個實例(不能創建對構造器的委托) object[] args = new object[] { 20 }; Console.WriteLine("x before constructor called:" + args[0]); dynamic obj = Activator.CreateInstance(t,args); Console.WriteLine("Type:" + obj.GetType().ToString()); Console.WriteLine("x after constructor returns:" + args[0]); //讀寫一個字段 try { obj.m_someField = 25; int value = Convert.ToInt16(obj.m_someField); Console.WriteLine("someField:" + value); } catch (Exception ex) { //字段是私有的,so會出錯的 Console.WriteLine("Failed to access field:" + ex.Message); } //調用一個方法 string str = obj.ToString(); Console.WriteLine("ToString:" + str); //讀寫一個屬性 try { obj.SomeProp =5; } catch (Exception) { Console.WriteLine("Property set catch!"); } obj.SomeProp = 30; int v = Convert.ToInt16(obj.SomeProp); Console.WriteLine("SomeProp:", v); //添加刪除事件 obj.SomeEvent += new EventHandler(EventCallback); obj.SomeEvent -= new EventHandler(EventCallback); } /// <summary> /// 事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void EventCallback(object sender, EventArgs e) { }
好了,反射知識暫告一段落,希望多提意見和建議。
