第二十三章 程序集加载和反射
2013-04-06
23.1 程序集加载
我们知道,JIT编译器将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型。在运行时,会利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所有引用类型。
在内部,CLR使用System.Reflection.Assembly类的静态方法Load来加载这个程序集。这个方法是CLR中与Win32 LoadLibrary函数等价的方法。以下是Load方法的几个重载版本:
1 public class Assembly2 {3 public static Assembly Load(AssemblyName assemblyRef);4 public static Assembly Load(String assemblyString);5 }
Load导致CLR向程序集应用一个版本绑定重定向策略,并在GAC(全局程序集缓存)中查看程序集。如果没找到,就去应用程序的基目录、私有路径子目录和codebase(参考3.9 高级管理控制)位置查找。如果调用Load时,传递的是一个弱命名的程序集,Load就不会向程序集应用一个版本绑定重定向策略,也不会去GAC中查找程序集。
在内部,CLR使用System.Reflection.Assembly类的另一个静态方法LoadFrom
1 public class Assembly2 {3 public static Assembly LoadFrom(String path);4 }
LoadFrom首先会调用System.Reflection.AssemblyName类的静态方法GetAssemblyName。该方法打开指定的文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息,然后返回一个System.Reflection.AssemblyName对象。随后在LoadFrom内部调用上面的Load方法。若能找到,加载;若不能,用实参传递的路径加载程序集。
另外,LoadFrom方法允许传递一个URL作为实参
1 Assembly a=Assembly.LoadFrom(@"http://Wintellect.com/SomeAssembly.dll");
上面代码,CLR会下载文件,将他安装到用户的下载缓存中,再从那儿下载。
23.2 使用反射构建动态可扩展应用程序
元数据是用一系列表来存储的。生成一个程序集或模块时,编译器会创建一个类型定义表、一个字段定义表、一个方法定义表及其他表。利用System.Reflection命名空间中包含的一些类型,可以写代码来反射(或者说”解析“)这些元数据表。
23.3 反射的性能
反射是相当强大的一个机制,它允许在运行时发现并使用编译器还不了解的类型及其成员。但是,它有以下两个缺点:
- 反射回造成编译时无法保证 类型安全性。由于反射严重依赖字符串,所以会丧失编译时类型安全性。如代码 Type.GetType("Jef"); 要求反射在程序集中查找一名为”Jef“的类型,但程序集中实际包含的是”Jeff“,代码编译时可通过,但运行时会出错。
- 反射速度慢。使用反射式,要用字符串标识每个类型及其成员,需要用它们来扫描程序集的元数据。用反射调用一个方法时,首先必须将参数打包成数组,再次将解包数组到线程栈上,此外,还要检验实参数据类型是否正确,最后,要确保调用者有安全权限调用被调用着。
基于上述原因,推荐用接口技术或基类技术代替反射:
- 让类型从一个编译时已知的基类型派生。在运行时,构造派生类型的一个实例,将对它的引用放到基类的一个变量中(利用转型),再调用基类型定义的虚方法。
- 让类型实现一个编译时已知的接口。将对它的引用放到接口的一个变量中(利用转型),再调用接口定义的方法。
在使用这两种技术时,强烈建议在接口或基类型自己的程序集中定义它们,这有助于缓解版本控制的问题。欲知详情,请参见节
23.3.1 发现程序集中定义的类型
1 internal static class DiscoverTypes { 2 public static void Go() { 3 String dataAssembly = "System.Data, version=4.0.0.0, " + 4 "culture=neutral, PublicKeyToken=b77a5c561934e089"; 5 LoadAssemAndShowPublicTypes(dataAssembly); 6 } 7 8 private static void LoadAssemAndShowPublicTypes(String assemId) { 9 // Explicitly load an assembly in to this AppDomain10 Assembly a = Assembly.Load(assemId);11 12 // Execute this loop once for each Type 13 // publicly-exported from the loaded assembly 14 foreach (Type t in a.GetExportedTypes()) {15 Console.WriteLine(t.FullName);16 }17 }18 }
23.3.2 类型对象的准确含义
一个类型在一个AppDomain1中被首次访问时,CLR会构造System.RuntimeType的一个实例,并初始化这个对象的字段,以反映这个类型的信息。我们知道,System.Object定义了一个公共非虚实例方法GetType。调用这个方法是,CLR会判断指定对象的类型,并返回对它的RuntimeType对象的一个引用。由于在一个AppDomain中,每个类型只有一个RuntimeType对象(类型对象,参考4.4 运行时相互关系)。
除了Object的GetType,FCL还提供了获得Type对象的其它方式:
- System.Type类型提供了静态方法GetType的几个重载版本。这个方法所有版本都接受一个String参数(指定类型的全名)。调用时,它首先会检查调用程序集是否有该类型,有,则返回一个恰当的RunTimeType对象的引用;没有,则检查MSCorLib.dll;再没有,返回null或System.TypeLoadException。
- C#还提供操作符typeof来获得对一个类型的引用,通常可用它来将晚期绑定的类型信息与早期绑定(编译时已知)的类型信息比较,如下代码:
1 private static void SomeMethod(Object o)2 {3 //GetType方法在运行时返回对象的类型(晚期绑定)4 //typeof方法返回指定类的类型(早期绑定)5 if(o.GetType()==typeof(FileInfo)){...}6 if(o.GetType()==typeof(DirecotryInfo)){...}7 }
23.3.3 构建Exception派生类型的一个层次结构
1 public static void Go() { 2 // Explicitly load the assemblies that we want to reflect over 3 Assembly[] assemblies= LoadAssemblies(); 4 5 // Recursively build the class hierarchy as a hyphen-separated string 6 FuncClassNameAndBase = null; 7 ClassNameAndBase = t => "-" + t.FullName + 8 ((t.BaseType != typeof(Object)) ? ClassNameAndBase(t.BaseType) : String.Empty); 9 10 // Define our query to find all the public Exception-derived types in this AppDomain's assemblies11 var exceptionTree =12 (from a in assemblies13 from t in a.GetExportedTypes()14 where t.IsClass && t.IsPublic && typeof(Exception).IsAssignableFrom(t)15 let typeHierarchyTemp = ClassNameAndBase(t).Split('-').Reverse().ToArray()16 let typeHierarchy = String.Join("-", typeHierarchyTemp, 0, typeHierarchyTemp.Length - 1)17 orderby typeHierarchy18 select typeHierarchy).ToArray();19 20 // Display the Exception tree21 Console.WriteLine("{0} Exception types found.", exceptionTree.Length);22 foreach (String s in exceptionTree) {23 // For this Exception type, split its base types apart24 String[] x = s.Split('-');25 26 // Indent based on # of base types and show the most-derived type27 Console.WriteLine(new String(' ', 3 * (x.Length - 1)) + x[x.Length - 1]);28 }29 }30 31 32 private static Assembly[] LoadAssemblies() {33 String[] assemblies = {34 "System, PublicKeyToken={0}",35 "System.Core, PublicKeyToken={0}",36 "System.Data, PublicKeyToken={0}",37 "System.Design, PublicKeyToken={1}",38 "System.DirectoryServices, PublicKeyToken={1}",39 "System.Drawing, PublicKeyToken={1}",40 "System.Drawing.Design, PublicKeyToken={1}",41 "System.Management, PublicKeyToken={1}",42 "System.Messaging, PublicKeyToken={1}",43 "System.Runtime.Remoting, PublicKeyToken={0}",44 "System.Security, PublicKeyToken={1}",45 "System.ServiceProcess, PublicKeyToken={1}",46 "System.Web, PublicKeyToken={1}",47 "System.Web.RegularExpressions, PublicKeyToken={1}",48 "System.Web.Services, PublicKeyToken={1}",49 "System.Windows.Forms, PublicKeyToken={0}",50 "System.Xml, PublicKeyToken={0}",51 };52 53 String EcmaPublicKeyToken = "b77a5c561934e089";54 String MSPublicKeyToken = "b03f5f7f11d50a3a";55 56 // Get the version of the assembly containing System.Object57 // We'll assume the same version for all the other assemblies58 Version version = typeof(System.Object).Assembly.GetName().Version;59 60 List lassems = new List ();61 62 // Explicitly load the assemblies that we want to reflect over63 foreach (String a in assemblies) 64 {65 String AssemblyIdentity =66 String.Format(a, EcmaPublicKeyToken, MSPublicKeyToken) +67 ", Culture=neutral, Version=" + version;68 lassems.Add(Assembly.Load(AssemblyIdentity)); 69 }70 return lassems.ToArray();71 }
23.3.4 构造类型的实例
System.Acitivator的静态方法CreateInstance ,调用该方法可构造类型的实例(除了数组和委托)。数组调用Array的静态方法CreateInstance;委托调用Delegate的静态方法CreateDelegate。
23.4 设计支持加载项的应用程序
构建可扩展的应用程序时,接口是中心。可用基类来代替接口,但接口通常是首选的,因为它允许加载项开发人员选择他们自己的基类。下面描述如何设计加载项应用程序:
- 创建一个“宿主SDK(Host SDK),它定义一个接口,接口的方法作为宿主应用程序域加载项之间的通信机制。一旦搞定接口,可为这个程序集赋予一个强名称(参见第3章),然后打包并部署到合作伙伴那里。一旦发布,就要避免对该程序集中类型做任何重大修改,例如,接口。但对参数或返回值定义了自己的数据类型,可添加。对程序集的任何修改,可能需要使用一个发布策略来部署它(参见第3章)。
- 加载项实现上面“宿主SDK”定义的接口。
- 宿主应用程序通过”宿主SDK“的接口来调用加载项的实现。
代码如下:
1)”宿主SDK“程序集HostSDK.dll代码:
1 using System;2 3 namespace Wintellect.HostSDK {4 public interface IAddIn {5 String DoSomething(Int32 x);6 }7 }
2)加载项AddInTypes.dll引用并实现HostSDK定义的接口,代码如下:
1 using System; 2 using Wintellect.HostSDK; 3 4 public class AddIn_A : IAddIn { 5 public AddIn_A() { 6 } 7 public String DoSomething(Int32 x) { 8 return "AddIn_A: " + x.ToString(); 9 }10 }11 12 public class AddIn_B : IAddIn {13 public AddIn_B() {14 }15 public String DoSomething(Int32 x) {16 return "AddIn_B: " + (x * 2).ToString();17 }18 }
3)宿主应用程序Host.exe引用HostSDK.dll,并动态加载加载项AddInType.dll。为了简化代码,假定加载项已部署到宿主的exe文件相同的目录。强烈建议研究一下Microsoft的MEF(托管可扩展性框架:Managed Extensiblity Framework)。
1 using System; 2 using System.IO; 3 using System.Reflection; 4 using System.Collections.Generic; 5 using Wintellect.HostSDK; 6 7 public sealed class Program { 8 public static void Main() { 9 // Find the directory that contains the Host exe10 String AddInDir = Path.GetDirectoryName(11 Assembly.GetEntryAssembly().Location);12 13 // Assume AddIn assemblies are in same directory as host's EXE file14 String[] AddInAssemblies = Directory.GetFiles(AddInDir, "*.dll");15 16 // Create a collection of usable Add-In Types17 ListAddInTypes = new List ();18 19 // Load Add-In assemblies; discover which types are usable by the host20 foreach (String file in AddInAssemblies) {21 Assembly AddInAssembly = Assembly.LoadFrom(file);22 23 // Examine each publicly-exported type24 foreach (Type t in AddInAssembly.GetExportedTypes()) {25 // If the type is a class that implements the IAddIn 26 // interface, then the type is usable by the host27 if (t.IsClass && typeof(IAddIn).IsAssignableFrom(t)) {28 AddInTypes.Add(t);29 }30 }31 }32 33 // Initialization complete: the host has discovered the usable Add-Ins34 35 // Here's how the host can construct Add-In objects and use them36 foreach (Type t in AddInTypes) {37 IAddIn ai = (IAddIn) Activator.CreateInstance(t);38 Console.WriteLine(ai.DoSomething(5));39 }40 }41 }
23.5 使用反射发现类型的成员
图1 封装了类型成员的反射类型的层次结构
以下程序演示了如何查询一个类型的成员与它们相关的一些信息。
1 using System; 2 using System.Reflection; 3 4 internal static class Program 5 { 6 public static void Main() 7 { 8 // Loop through all assemblies loaded in this AppDomain 9 Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();10 foreach (Assembly a in assemblies) 11 {12 Show(0, "Assembly: {0}", a);13 14 // Find Types in the assembly15 foreach (Type t in a.GetExportedTypes()) 16 {17 Show(1, "Type: {0}", t);18 19 // Indicate the kinds of members we want to discover20 const BindingFlags bf = BindingFlags.DeclaredOnly |21 BindingFlags.NonPublic | BindingFlags.Public |22 BindingFlags.Instance | BindingFlags.Static;23 24 // Discover the type's members25 foreach (MemberInfo mi in t.GetMembers(bf)) 26 {27 String typeName = String.Empty;28 if (mi is Type) typeName = "(Nested) Type";29 if (mi is FieldInfo) typeName = "FieldInfo";30 if (mi is MethodInfo) typeName = "MethodInfo";31 if (mi is ConstructorInfo) typeName = "ConstructoInfo";32 if (mi is PropertyInfo) typeName = "PropertyInfo";33 if (mi is EventInfo) typeName = "EventInfo";34 35 Show(2, "{0}: {1}", typeName, mi);36 }37 }38 }39 }40 41 private static void Show(Int32 indent, String format, params Object[] args) 42 {43 Console.WriteLine(new String(' ', 3 * indent) + format, args);44 }45 }
Type除了GetMembers方法返回类型的所有成员外,还提供了一些方法能返回特定的类型成员:GetnestedTypes,GetFields,GetConstructors,GetMethods,GetProperties以及GetEvents方法 。
23.5.2 BindingFlags: 筛选返回的成员类型
默认设置时BindingFlags.Public|BindingFlag.Instance|BindingFlags.Static。
23.5.3 发现类型的接口
为获得类型继承的接口,可调用type类型的FindInterfaces或GetInterfaces方法。这些方法返回接口的type对象。注意:这些方法扫描类型的继承层次结构,并返回在指定类型及其所有基类型上定义的所有接口。
判定一个类型那些成员实现了那个特定接口有点复杂,因为多个接口可能定义同一个方法。为获得特定接口的MethodInfo对象,可调用GetInterfaceMap实例方法,它返回System.Reflection.InterfaceMapping的一个实例,它定义了四个字段,见如下实例:
1 using System; 2 using System.Reflection; 3 4 internal static class InterfaceDiscover 5 { 6 // Define two interfaces for testing 7 private interface IBookRetailer : IDisposable { 8 void Purchase(); 9 void ApplyDiscount();10 }11 12 private interface IMusicRetailer {13 void Purchase();14 }15 16 // This class implements 2 interfaces defined by this assembly and 1 interface defined by another assembly17 private sealed class MyRetailer : IBookRetailer, IMusicRetailer, IDisposable {18 // IBookRetailer methods19 void IBookRetailer.Purchase() { }20 public void ApplyDiscount() { }21 22 // IMusicRetailer method23 void IMusicRetailer.Purchase() { }24 25 // IDisposable method26 public void Dispose() { }27 28 // MyRetailer method (not an interface method)29 public void Purchase() { }30 }31 32 public static void Go() {33 // Find interfaces implemented by MyRetailer where the interface is defined in our own assembly.34 // This is accomplished using a delegate to a filter method that we pass to FindInterfaces.35 Type t = typeof(MyRetailer);36 Type[] interfaces = t.FindInterfaces(TypeFilter, typeof(InterfaceDiscover).Assembly);37 Console.WriteLine("MyRetailer implements the following interfaces (defined in this assembly):\n");38 39 // Show information about each interface40 foreach (Type i in interfaces) {41 InterfaceMapping map = t.GetInterfaceMap(i);42 Console.WriteLine(" Interface '{0}' is implemented by class '{1}'", map.InterfaceType, map.TargetType);43 44 for (Int32 m = 0; m < map.InterfaceMethods.Length; m++) {45 // Display the interface method name and which type method implements the interface method.46 Console.WriteLine(" {0} is implemented by {1}",47 map.InterfaceMethods[m], map.TargetMethods[m]);48 }49 }50 }51 52 // Returns true if type matches filter criteria53 private static Boolean TypeFilter(Type t, Object filterCriteria) {54 // Return true if the interface is defined in the same assembly identified by filterCriteria55 return t.Assembly == (Assembly)filterCriteria;56 }57 }
结果:
23.5.4 调用类型的成员
发现类型成员后,你可能想调用其中一个成员。”调用“(invoke)的含义取决于调用成员的种类。如下表所示:
成员类型 | 用于调用成员的方法 |
FieldInfo | 调用GetValue获取字段的值 调用SetValue设置字段的值 |
PropertyInfo | 调用GetValue调用属性的get访问器方法 调用SetValue调用属性的set访问器方法 |
EventInfo | 调用AddEventHandler调用事件的add访问器方法 调用RemoveEventHandler调用事件的remove访问器方法 |
ConstructorInfo | 调用Invoke构造类型的一个实例,并调用一个构造器 |
MethodInfo | 调用Invoke调用类型的一个方法 |
Type 的 InvokeMember() 实例方法,可通过它调用一个成员。 该方法会执行两个操作:
1)绑定成员:选择要调用的一个恰当的成员
2)调用:实际调用成员
23.5.5 一次绑定,多次调用
Type的InvokeMember实例方法,通过内在的绑定和调用两步操作实现成员的调用。为了提高性能,可以实现一次绑定,多次调用。 看如下代码:
1 using System; 2 using System.Reflection; 3 using Microsoft.CSharp.RuntimeBinder; 4 5 internal static class Invoker { 6 // This class is used to demonstrate reflection 7 // It has a field, constructor, method, property, and an event 8 private sealed class SomeType { 9 private Int32 m_someField; 10 public SomeType(ref Int32 x) { x *= 2; } 11 public override String ToString() { return m_someField.ToString(); } 12 public Int32 SomeProp { 13 get { return m_someField; } 14 set { 15 if (value < 1) throw new ArgumentOutOfRangeException("value", "value must be > 0"); 16 m_someField = value; 17 } 18 } 19 public event EventHandler SomeEvent; 20 private void NoCompilerWarnings() { 21 SomeEvent.ToString(); 22 } 23 } 24 25 private const BindingFlags c_bf = BindingFlags.DeclaredOnly | BindingFlags.Public | 26 BindingFlags.NonPublic | BindingFlags.Instance; 27 28 public static void Go() { 29 Type t = typeof(SomeType); 30 //演示如何利用Type的InvokeMember来绑定并调用一个成员 31 UseInvokeMemberToBindAndInvokeTheMember(t); 32 Console.WriteLine(); 33 //演示绑定到一个成员然后调用它。适用于一次绑定,多次调用 34 BindToMemberThenInvokeTheMember(t); 35 Console.WriteLine(); 36 //演示绑定到一个成员,然后创建一个委托来引用该成员,通过委托调用之。速度比上一个更快 37 BindToMemberCreateDelegateToMemberThenInvokeTheMember(t); 38 Console.WriteLine(); 39 //演示使用C#的dynamic基本类型来简化访问成员时使用语法。 40 UseDynamicToBindAndInvokeTheMember(t); 41 Console.WriteLine(); 42 } 43 44 private static void UseInvokeMemberToBindAndInvokeTheMember(Type t) { 45 Console.WriteLine("UseInvokeMemberToBindAndInvokeTheMember"); 46 47 // Construct an instance of the Type 48 Object[] args = new Object[] { 12 }; // Constructor arguments 49 Console.WriteLine("x before constructor called: " + args[0]); 50 Object obj = t.InvokeMember(null, c_bf | BindingFlags.CreateInstance, null, null, args); 51 Console.WriteLine("Type: " + obj.GetType().ToString()); 52 Console.WriteLine("x after constructor returns: " + args[0]); 53 54 // Read and write to a field 55 t.InvokeMember("m_someField", c_bf | BindingFlags.SetField, null, obj, new Object[] { 5 }); 56 Int32 v = (Int32)t.InvokeMember("m_someField", c_bf | BindingFlags.GetField, null, obj, null); 57 Console.WriteLine("someField: " + v); 58 59 // Call a method 60 String s = (String)t.InvokeMember("ToString", c_bf | BindingFlags.InvokeMethod, null, obj, null); 61 Console.WriteLine("ToString: " + s); 62 63 // Read and write a property 64 try { 65 t.InvokeMember("SomeProp", c_bf | BindingFlags.SetProperty, null, obj, new Object[] { 0 }); 66 } 67 catch (TargetInvocationException e) { 68 if (e.InnerException.GetType() != typeof(ArgumentOutOfRangeException)) throw; 69 Console.WriteLine("Property set catch."); 70 } 71 t.InvokeMember("SomeProp", c_bf | BindingFlags.SetProperty, null, obj, new Object[] { 2 }); 72 v = (Int32)t.InvokeMember("SomeProp", c_bf | BindingFlags.GetProperty, null, obj, null); 73 Console.WriteLine("SomeProp: " + v); 74 75 // Add and remove a delegate from the event by invoking the event抯 add/remove methods 76 EventHandler eh = new EventHandler(EventCallback); 77 t.InvokeMember("add_SomeEvent", c_bf | BindingFlags.InvokeMethod, null, obj, new Object[] { eh }); 78 t.InvokeMember("remove_SomeEvent", c_bf | BindingFlags.InvokeMethod, null, obj, new Object[] { eh }); 79 } 80 81 private static void BindToMemberThenInvokeTheMember(Type t) { 82 Console.WriteLine("BindToMemberThenInvokeTheMember"); 83 84 // Construct an instance 85 // ConstructorInfo ctor = t.GetConstructor(new Type[] { Type.GetType("System.Int32&") }); 86 ConstructorInfo ctor = t.GetConstructor(new Type[] { typeof(Int32).MakeByRefType() }); 87 Object[] args = new Object[] { 12 }; // Constructor arguments 88 Console.WriteLine("x before constructor called: " + args[0]); 89 Object obj = ctor.Invoke(args); 90 Console.WriteLine("Type: " + obj.GetType().ToString()); 91 Console.WriteLine("x after constructor returns: " + args[0]); 92 93 // Read and write to a field 94 FieldInfo fi = obj.GetType().GetField("m_someField", c_bf); 95 fi.SetValue(obj, 33); 96 Console.WriteLine("someField: " + fi.GetValue(obj)); 97 98 // Call a method 99 MethodInfo mi = obj.GetType().GetMethod("ToString", c_bf);100 String s = (String)mi.Invoke(obj, null);101 Console.WriteLine("ToString: " + s);102 103 // Read and write a property104 PropertyInfo pi = obj.GetType().GetProperty("SomeProp", typeof(Int32));105 try {106 pi.SetValue(obj, 0, null);107 }108 catch (TargetInvocationException e) {109 if (e.InnerException.GetType() != typeof(ArgumentOutOfRangeException)) throw;110 Console.WriteLine("Property set catch.");111 }112 pi.SetValue(obj, 2, null);113 Console.WriteLine("SomeProp: " + pi.GetValue(obj, null));114 115 // Add and remove a delegate from the event116 EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf);117 EventHandler eh = new EventHandler(EventCallback); // See ei.EventHandlerType118 ei.AddEventHandler(obj, eh);119 ei.RemoveEventHandler(obj, eh);120 }121 122 private static void BindToMemberCreateDelegateToMemberThenInvokeTheMember(Type t) {123 Console.WriteLine("BindToMemberCreateDelegateToMemberThenInvokeTheMember");124 125 // Construct an instance (You can't create a delegate to a constructor)126 Object[] args = new Object[] { 12 }; // Constructor arguments127 Console.WriteLine("x before constructor called: " + args[0]);128 Object obj = Activator.CreateInstance(t, args);129 Console.WriteLine("Type: " + obj.GetType().ToString());130 Console.WriteLine("x after constructor returns: " + args[0]);131 132 // NOTE: You can't create a delegate to a field133 134 // Call a method135 MethodInfo mi = obj.GetType().GetMethod("ToString", c_bf);136 var toString = (Func) Delegate.CreateDelegate(typeof(Func ), obj, mi);137 String s = toString();138 Console.WriteLine("ToString: " + s);139 140 // Read and write a property141 PropertyInfo pi = obj.GetType().GetProperty("SomeProp", typeof(Int32));142 var setSomeProp = (Action )Delegate.CreateDelegate(typeof(Action ), obj, pi.GetSetMethod());143 try {144 setSomeProp(0);145 }146 catch (ArgumentOutOfRangeException) {147 Console.WriteLine("Property set catch.");148 }149 setSomeProp(2);150 var getSomeProp = (Func )Delegate.CreateDelegate(typeof(Func ), obj, pi.GetGetMethod());151 Console.WriteLine("SomeProp: " + getSomeProp());152 153 // Add and remove a delegate from the event154 EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf);155 var addSomeEvent = (Action )Delegate.CreateDelegate(typeof(Action ), obj, ei.GetAddMethod());156 addSomeEvent(EventCallback);157 var removeSomeEvent = (Action )Delegate.CreateDelegate(typeof(Action ), obj, ei.GetRemoveMethod());158 removeSomeEvent(EventCallback); 159 }160 161 private static void UseDynamicToBindAndInvokeTheMember(Type t) {162 Console.WriteLine("UseDynamicToBindAndInvokeTheMember");163 164 // Construct an instance (You can't create a delegate to a constructor)165 Object[] args = new Object[] { 12 }; // Constructor arguments166 Console.WriteLine("x before constructor called: " + args[0]);167 dynamic obj = Activator.CreateInstance(t, args);168 Console.WriteLine("Type: " + obj.GetType().ToString());169 Console.WriteLine("x after constructor returns: " + args[0]);170 171 // Read and write to a field 172 try {173 obj.m_someField = 5;174 Int32 v = (Int32)obj.m_someField;175 Console.WriteLine("someField: " + v);176 }177 catch (RuntimeBinderException e) {178 // We get here because the field is private179 Console.WriteLine("Failed to access field: " + e.Message);180 }181 182 // Call a method183 String s = (String)obj.ToString();184 Console.WriteLine("ToString: " + s);185 186 // Read and write a property187 try {188 obj.SomeProp = 0;189 }190 catch (ArgumentOutOfRangeException) {191 Console.WriteLine("Property set catch.");192 }193 obj.SomeProp = 2;194 Int32 val = (Int32)obj.SomeProp;195 Console.WriteLine("SomeProp: " + val);196 197 // Add and remove a delegate from the event198 obj.SomeEvent += new EventHandler(EventCallback);199 obj.SomeEvent -= new EventHandler(EventCallback);200 }201 202 // Callback method added to the event203 private static void EventCallback(Object sender, EventArgs e) { }204 }