23.1 程序集加载
我们知道,JIT编译器将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型。在运行时,会利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所有引用类型。
在内部,CLR使用System.Reflection.Assembly类的静态方法Load来加载这个程序集。这个方法是CLR中与Win32 LoadLibrary函数等价的方法。以下是Load方法的几个重载版本:
1 public class Assembly 2 { 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 Assembly 2 { 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会下载文件,将他安装到用户的下载缓存中,再从那儿下载。
C#反射-Assembly.Load、LoadFrom与LoadFile进阶
23.2 使用反射构建动态可扩展应用程序
元数据是用一系列表来存储的。生成一个程序集或模块时,编译器会创建一个类型定义表、一个字段定义表、一个方法定义表及其他表。利用System.Reflection命名空间中包含的一些类型,可以写代码来反射(或者说”解析“)这些元数据表。
23.3 反射的性能
反射是相当强大的一个机制,它允许在运行时发现并使用编译器还不了解的类型及其成员。但是,它有以下两个缺点:
- 反射回造成编译时无法保证 类型安全性。由于反射严重依赖字符串,所以会丧失编译时类型安全性。如代码 Type.GetType("Jef"); 要求反射在程序集中查找一名为”Jef“的类型,但程序集中实际包含的是”Jeff“,代码编译时可通过,但运行时会出错。
- 反射速度慢。使用反射式,要用字符串标识每个类型及其成员,需要用它们来扫描程序集的元数据。用反射调用一个方法时,首先必须将参数打包成数组,再次将解包数组到线程栈上,此外,还要检验实参数据类型是否正确,最后,要确保调用者有安全权限调用被调用着。
基于上述原因,推荐用接口技术或基类技术代替反射:
- 让类型从一个编译时已知的基类型派生。在运行时,构造派生类型的一个实例,将对它的引用放到基类的一个变量中(利用转型),再调用基类型定义的虚方法。
- 让类型实现一个编译时已知的接口。将对它的引用放到接口的一个变量中(利用转型),再调用接口定义的方法。
在使用这两种技术时,强烈建议在接口或基类型自己的程序集中定义它们,这有助于缓解版本控制的问题。欲知详情,请参见23.4节
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 AppDomain 10 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 Func<Type, String> ClassNameAndBase = 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 assemblies 11 var exceptionTree = 12 (from a in assemblies 13 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 typeHierarchy 18 select typeHierarchy).ToArray(); 19 20 // Display the Exception tree 21 Console.WriteLine("{0} Exception types found.", exceptionTree.Length); 22 foreach (String s in exceptionTree) { 23 // For this Exception type, split its base types apart 24 String[] x = s.Split('-'); 25 26 // Indent based on # of base types and show the most-derived type 27 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.Object 57 // We'll assume the same version for all the other assemblies 58 Version version = typeof(System.Object).Assembly.GetName().Version; 59 60 List<Assembly> lassems = new List<Assembly>(); 61 62 // Explicitly load the assemblies that we want to reflect over 63 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 exe 10 String AddInDir = Path.GetDirectoryName( 11 Assembly.GetEntryAssembly().Location); 12 13 // Assume AddIn assemblies are in same directory as host's EXE file 14 String[] AddInAssemblies = Directory.GetFiles(AddInDir, "*.dll"); 15 16 // Create a collection of usable Add-In Types 17 List<Type> AddInTypes = new List<Type>(); 18 19 // Load Add-In assemblies; discover which types are usable by the host 20 foreach (String file in AddInAssemblies) { 21 Assembly AddInAssembly = Assembly.LoadFrom(file); 22 23 // Examine each publicly-exported type 24 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 host 27 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-Ins 34 35 // Here's how the host can construct Add-In objects and use them 36 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 assembly 15 foreach (Type t in a.GetExportedTypes()) 16 { 17 Show(1, "Type: {0}", t); 18 19 // Indicate the kinds of members we want to discover 20 const BindingFlags bf = BindingFlags.DeclaredOnly | 21 BindingFlags.NonPublic | BindingFlags.Public | 22 BindingFlags.Instance | BindingFlags.Static; 23 24 // Discover the type's members 25 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 assembly 17 private sealed class MyRetailer : IBookRetailer, IMusicRetailer, IDisposable { 18 // IBookRetailer methods 19 void IBookRetailer.Purchase() { } 20 public void ApplyDiscount() { } 21 22 // IMusicRetailer method 23 void IMusicRetailer.Purchase() { } 24 25 // IDisposable method 26 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 interface 40 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 criteria 53 private static Boolean TypeFilter(Type t, Object filterCriteria) { 54 // Return true if the interface is defined in the same assembly identified by filterCriteria 55 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 property 104 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 event 116 EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf); 117 EventHandler eh = new EventHandler(EventCallback); // See ei.EventHandlerType 118 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 arguments 127 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 field 133 134 // Call a method 135 MethodInfo mi = obj.GetType().GetMethod("ToString", c_bf); 136 var toString = (Func<String>) Delegate.CreateDelegate(typeof(Func<String>), obj, mi); 137 String s = toString(); 138 Console.WriteLine("ToString: " + s); 139 140 // Read and write a property 141 PropertyInfo pi = obj.GetType().GetProperty("SomeProp", typeof(Int32)); 142 var setSomeProp = (Action<Int32>)Delegate.CreateDelegate(typeof(Action<Int32>), 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<Int32>)Delegate.CreateDelegate(typeof(Func<Int32>), obj, pi.GetGetMethod()); 151 Console.WriteLine("SomeProp: " + getSomeProp()); 152 153 // Add and remove a delegate from the event 154 EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf); 155 var addSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, ei.GetAddMethod()); 156 addSomeEvent(EventCallback); 157 var removeSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), 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 arguments 166 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 private 179 Console.WriteLine("Failed to access field: " + e.Message); 180 } 181 182 // Call a method 183 String s = (String)obj.ToString(); 184 Console.WriteLine("ToString: " + s); 185 186 // Read and write a property 187 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 event 198 obj.SomeEvent += new EventHandler(EventCallback); 199 obj.SomeEvent -= new EventHandler(EventCallback); 200 } 201 202 // Callback method added to the event 203 private static void EventCallback(Object sender, EventArgs e) { } 204 }