• 《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 }
  • 相关阅读:
    Max关闭WPF
    InstallShield安装过程介绍
    InstallShield相关资料整理
    .net reflection的一点研究
    (转)VMware增加磁盘容量方法
    领域驱动设计《读书笔记》
    《领域驱动设计C#2008实现》读书笔记
    深入研究c++对象模型
    <转载>com之包容聚合
    基于插件开发的架构研究
  • 原文地址:https://www.cnblogs.com/Ming8006/p/3002913.html
Copyright © 2020-2023  润新知