《CLR via C#》读书笔记 之 程序集加载和反射 明

第二十三章 程序集加载和反射

2013-04-06

23.1 程序集加载
23.2 使用反射构建动态可扩展应用程序
23.3 反射的性能 
  23.3.1 发现程序集中定义的类型
  23.3.2 类型对象的准确含义
  23.3.3 构建Exception派生类型的一个层次结构
  23.3.4 构造类型的实例
23.4 设计支持加载项的应用程序
23.5 使用反射发现类型的成员
  23.5.2 BindingFlags: 帅选返回的成员类型
  23.5.3 发现类型的接口
  23.5.4 调用类型的成员
  23.5.5 一次绑定,多次调用

23.1 程序集加载


返回

我们知道,JIT编译器将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型。在运行时,会利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所有引用类型。

在内部,CLR使用System.Reflection.Assembly类的静态方法Load来加载这个程序集。这个方法是CLR中与Win32 LoadLibrary函数等价的方法。以下是Load方法的几个重载版本:

View Code
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

View Code
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作为实参

View Code
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 发现程序集中定义的类型

View Code
 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来获得对一个类型的引用,通常可用它来将晚期绑定的类型信息与早期绑定(编译时已知)的类型信息比较,如下代码:
View Code
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派生类型的一个层次结构

View Code
 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代码:

View Code
1 using System;
2 
3 namespace Wintellect.HostSDK {
4    public interface IAddIn {
5       String DoSomething(Int32 x);
6    }
7 }

2)加载项AddInTypes.dll引用并实现HostSDK定义的接口,代码如下:

View Code
 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)。

View Code
 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 封装了类型成员的反射类型的层次结构 

 以下程序演示了如何查询一个类型的成员与它们相关的一些信息。

View Code
 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的一个实例,它定义了四个字段,见如下实例:

View Code
 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实例方法,通过内在的绑定和调用两步操作实现成员的调用。为了提高性能,可以实现一次绑定,多次调用。 看如下代码:

View Code
  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 }
原文地址:https://www.cnblogs.com/Ming8006/p/3002913.html