(译)一个通用快速的反射方法调用

   =============C#.Net 篇目录==============

 

原文:http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker

 

源码下载:Download source and performance test project - 4.04 Kb

介绍

         有时,我们会碰见需要动态调用对象方法的场景,而这个方法只有在运行的时候才能得知。通常的,会使用方法的反射调用,但是这通常会导致程序速度变慢。这篇文章将介绍一种高效替代方案----动态方法调用。

 

背景环境

         当我读到文章Fast Dynamic Property Accessors时,我想到我项目中在循环中运用了大量方法的反射调用,然而这样调用是毫无效率的。DynamicMethod 提醒我是否可在方法调用前使用System.Reflection.Emit 生成DynamicMethod绑定到指定的方法,这样或许能提高程序性能。

 

代码

首先,使用反射找到将要调用的方法成员:

1
MethodInfo methodInfo = typeof(Person).GetMethod("Say");

然后,创建动态方法并且返回调用该动态方法的委托:

1
2
FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo);
fastInvoker(new Person(), new object[]{"hello"});

代替之前方法的反射调用:

1
methodInfo.Invoke(new Person(), new object[]{"hello"});

 

实现

首先,我们需要为动态方法定义一个适当的委托:

public delegate object FastInvokeHandler(object target, object[] paramters);

为了不改变之前方法的反射调用模式,所以我们定义的委托接收参数和返回值类似MethodInfo.invoke() 。

         下面贴出 DynamicMethod 生成代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo)
{
    DynamicMethod dynamicMethod = new DynamicMethod(string.Empty,
                     typeof(object), new Type[] { typeof(object),
                     typeof(object[]) },
                     methodInfo.DeclaringType.Module);
    ILGenerator il = dynamicMethod.GetILGenerator();
    ParameterInfo[] ps = methodInfo.GetParameters();
    Type[] paramTypes = new Type[ps.Length];
    for (int i = 0; i < paramTypes.Length; i++)
    {
        paramTypes[i] = ps[i].ParameterType;
    }
    LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];
    for (int i = 0; i < paramTypes.Length; i++)
    {
        locals[i] = il.DeclareLocal(paramTypes[i]);
    }
    for (int i = 0; i < paramTypes.Length; i++)
    {
        il.Emit(OpCodes.Ldarg_1);
        EmitFastInt(il, i);
        il.Emit(OpCodes.Ldelem_Ref);
        EmitCastToReference(il, paramTypes[i]);
        il.Emit(OpCodes.Stloc, locals[i]);
    }
    il.Emit(OpCodes.Ldarg_0);
    for (int i = 0; i < paramTypes.Length; i++)
    {
        il.Emit(OpCodes.Ldloc, locals[i]);
    }
    il.EmitCall(OpCodes.Call, methodInfo, null);
    if (methodInfo.ReturnType == typeof(void))
        il.Emit(OpCodes.Ldnull);
    else
        EmitBoxIfNeeded(il, methodInfo.ReturnType);
    il.Emit(OpCodes.Ret);
    FastInvokeHandler invoder =
      (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
    return invoder;
}

 

总结        

         好了,我想这个通用方法可以代替大多数方法的反射同时会获得大约50倍的效率提高,欢迎反馈任何改善的建议。

         另外值得注意的优势(感谢MaxGuernsey的提醒):如果你调用的方法内部抛出异常,FastInovker 会抛出具体错误信息,然而Method.Invoke 仅仅只会抛出“调用目标发生异常(TargetInvocationException)”。

 

作者:Luyan

 

      相关链接:

                   《(9)程序集的加载和反射》

C#.Net 篇
涉及微软 C#.Net 相关知识文章,如反射机制、垃圾回收、插件机制、多线程编程等等
(译)一个通用快速的反射方法调用
摘要: =============C#.Net 篇目录==============原文:http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker源码下载:Download source and performance test project - 4.04 Kb 介绍 有时,我们会碰见需要动态调用对象方法的场景,而这个方法只有在运行的时候才能得知。通常的,会使用方法的反射调用,但是这通常会导致程序速度变慢。这篇文章将介绍一种高效替代方案----动态方法调用。背景环境 当我读到文章《Fast Dynamic Prope阅读全文
posted @ 2012-04-03 10:03 滴答的雨 阅读(1096) | 评论 (9) 编辑
 
C#.Net 篇目录
摘要: 路漫漫其修远兮...记下脚印!!!反射机制(1)程序集基础知识(2)强名称程序集与数字证书(3)程序集加载 Assembly类(4)绑定程序集配置策略(5)CLR 运行时探测程序集引用的步骤(6)程序集加载上下文(7)动态程序集加载Load()(8)程序集反射 Type 类(9)程序集的加载和反射进阶: (译)一个通用快速的反射方法调用 相关链接: (3)如何在程序中使用数字证书 水漂收集 -- csc.exe(C# 编译器) 水漂收集 -- SignTool.exe(签名工具) 水漂收集 -- Sn.exe(强名称工具)阅读全文
posted @ 2012-04-01 10:16 滴答的雨 阅读(41) | 评论 (0) 编辑
 
水漂收集 -- Sn.exe(强名称工具)
摘要: =============C#.Net 篇目录==============Strong Name实用工具 (Sn.exe) 有助于使用强名称对程序集进行签名。Sn.exe 提供用于密钥管理、签名生成和签名验证的选项。所有 Sn.exe 选项都区分大小写,生成密钥对默认放在temp环境变量路径。(生成密钥时,会调用Windows提供的Crypto API)后缀:*.snk ,包含二进制形式的公钥和私钥。用法: sn [-q|-quiet] <option> [<parameters>]-c [<csp>]将默认加密服务提供程序 (CSP) 设置为用于强名称签名阅读全文
posted @ 2012-04-01 09:49 滴答的雨 阅读(8) | 评论 (0) 编辑
 
水漂收集 -- SignTool.exe(签名工具)
摘要: =============C#.Net 篇目录==============签名工具是一个命令行工具,用于用证书对文件进行数字签名,验证文件和时间戳文件中的签名。用法:signtool [command] [options] [file_name | ...]command指定要对文件执行的操作的四个命令之一(catdb、sign、Timestamp 或 Verify)。options用于修改命令的选项。除了全局 /q 和 /v 选项之外,每个命令均支持一组唯一选项。/q 执行成功时不生成输出,执行失败时生成最少的输出。/v 执行成功、执行失败或产生警告消息时生成详细输出。file_name要进阅读全文
posted @ 2012-04-01 09:35 滴答的雨 阅读(105) | 评论 (0) 编辑
 
水漂收集 -- csc.exe(C# 编译器)
摘要: =============C#.Net 篇目录==============1. 两个代码优化选项托管代码的两个优化选项将方法的首次编译开销保持在最低限度,并且JIT编译器能判别CPU的指令集,并生成相应本地代码利用能提升程序性能的特殊指令,相对非托管代码(eg:C++是针对一种具体CPU【平台】编译的,一旦调用,代码直接就能执行)其效率相差不大,甚至因为一些特殊指令性能更好。1) /optimize影响IL代码的优化,优化后EXE/DLL文件更小,查看IL代码更易理解。影响JIT本地代码的优化;未优化的IL代码中包含许多NOP(no-operation,空操作)指令;还包含许多分支指令,它们用阅读全文
posted @ 2012-04-01 09:24 滴答的雨 阅读(12) | 评论 (0) 编辑
 
(9)程序集的加载和反射
摘要: =============C#.Net 篇目录==============一、程序集的加载程序集是 .NET Framework 应用程序的构造块;程序集构成了部署、版本控制、重复使用、激活范围控制和安全权限的基本单元。绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。根据此过程是发生在编译时还是运行时分为:a) 静态绑定:在生成时,编译器在程序集清单的元数据中记录静态引用。b) 动态绑定:由于调用各种方法而动态构造的,EG: Assembly.Load 方法。程序集如何加载请参见下面链接:1、 《(5)CLR 运行时探测程序集引用的步骤》2、 《(6)程序集加载上下文》3、 《(7)动阅读全文
posted @ 2012-03-31 20:56 滴答的雨 阅读(73) | 评论 (0) 编辑
 
(8)程序集反射 Type 类
摘要: =============C#.Net 篇目录==============Type 类表示类型声明:类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。这个类是线程安全的。Type 为 System.Reflection 功能的根,也是访问元数据的主要方式。使用 Type 的成员获取关于类型声明的信息,如构造函数、方法、字段、属性和类的事件,以及在其中部署该类的模块和程序集。Type 是允许多个实现的抽象基类。在反射时,并不直接使用Type类,使用的是公共语言运行时 (CLR) 提供的类型。例如,使用 C# 的 typeof 运算符获取 Type阅读全文
posted @ 2012-03-31 20:46 滴答的雨 阅读(21) | 评论 (0) 编辑
 
(7)动态程序集加载Load()
摘要: =============C#.Net 篇目录============== 上篇文章 《(6)程序集加载上下文》 已经告诉了我们各种程序集上下文,现在来看看.Net中是如何支持这些上下文的。一、Assembly类提供的多个载入动态程序集方法1. Load(),LoadFile(),LoadFrom(),LoadWithPartialName(),ReflectionOnlyLoad(),ReflectionOnlyLoadFrom(),UnsafeLoadFrom()1) Load()可通过程序集的唯一标识符AssemblyName对象或程序集的长|短格式名称字符串或基于通用对象文件格式(C.阅读全文
posted @ 2012-03-31 20:27 滴答的雨 阅读(42) | 评论 (0) 编辑
 
(6)程序集加载上下文
摘要: =============C#.Net 篇目录==============1. 四种程序集加载到上下文及优缺点:1) 默认加载上下文加载上下文包含通过探测全局程序集缓存、主机程序集存储区(如果承载运行时)以及应用程序域的 ApplicationBase 和 PrivateBinPath 所找到的程序集。比如Load()使用程序集标识的重载。(探测规则请参见:《(5)CLR 运行时探测程序集引用的步骤》 )使用默认加载上下文具有以下缺点:a) 加载到其他上下文中的依赖项不可用。b) 不能将探测路径外部的位置的程序集加载到默认加载上下文中。2) 加载位置上下文可以从未位于应用程序路径下(并因此未包阅读全文
posted @ 2012-03-31 20:16 滴答的雨 阅读(21) | 评论 (0) 编辑
 
(5)CLR 运行时探测程序集引用的步骤
摘要: =============C#.Net 篇目录==============l 开始绑定当运行时尝试解析对另一个程序集的引用时,就开始进行定位并绑定到程序集的进程。该引用可以是静态的,也可以是动态的。无论引用是对静态程序集的引用还是对动态程序集的引用,运行时均使用相同的解析过程。a) 静态引用:在生成时,编译器在程序集清单的元数据中记录静态引用。b) 动态引用:由于调用各种方法而动态构造的,加载方法详细介绍请参见 《(7)动态程序集加载Load()》 。l 运行时使用以下步骤来【解析】程序集引用: 第 1 步:检查配置文件,确定版本可以基于三个 XML 文件在不同的级别下配置程序集绑定行为 :.阅读全文
posted @ 2012-03-31 19:56 滴答的雨 阅读(20) | 评论 (0) 编辑
 
(4)绑定程序集配置策略
摘要: =============C#.Net 篇目录==============<runtime> 的 <assemblyBinding> 元素Xmlns特性,指定程序集绑定所需的 XML 命名空间。使用字符串“urn:schemas-microsoft-com:asm.v1”作为值。<assemblyBinding>子元素如下:1. <dependentAssembly> 元素封装每个程序集的绑定策略和程序集位置。为每个程序集使用一个 dependentAssembly 元素。a) <assemblyIdentity> 元素b) <阅读全文
posted @ 2012-03-31 19:38 滴答的雨 阅读(20) | 评论 (0) 编辑
 
(3)程序集加载 Assembly类
摘要: =============C#.Net 篇目录==============程序集是 .NET Framework 应用程序的构造块;程序集构成了部署、版本控制、重复使用、激活范围控制和安全权限的基本单元。属性:CodeBase,EntryPoint,EscapeCodeBase,Evidence,FullName,GlobalAssemblyCache,HostContext,ImageRuntimeVersion,IsDynamic,IsFullTrusted,Location,ManifestModule,PermissionSet,ReflectionOnly,SecurityRuleS阅读全文
posted @ 2012-03-31 19:29 滴答的雨 阅读(90) | 评论 (0) 编辑
 
(2)强名称程序集与数字证书
摘要: =============C#.Net 篇目录==============防止程序集被篡改,可以用两种不同但相互补充的方式对程序集进行签名:使用强名称或使用SignTool.exe(签名工具)可以将强名称的数字签名和使用SignTool.exe(签名工具)的证书签名一起提供给程序集,或者您可以单独使用其中之一。这两个签名工具一次只能对一个文件进行签名,对于多文件程序集,您可以对包含程序集清单的文件进行签名 (CLR总是首先加载包含“清单”元素据表的文件,再根据这个“清单”来获取程序集中的其他文件的名称) 。强名称签名的密钥【不必与】签名工具的微软代码签名证书密钥相同。一、强名称程序集1. 强名阅读全文
posted @ 2012-03-31 19:20 滴答的雨 阅读(84) | 评论 (0) 编辑
 
(1)程序集基础知识
摘要: =============C#.Net 篇目录==============程序集是 .NET Framework 应用程序的构造块;程序集构成了部署、版本控制、重复使用、激活范围控制和安全权限的基本单元。最终由CLR管理这些程序集中代码的执行。这意味着必须在目标机器上安装好 .NET Framework 。公共语言运行时(Common Language Runtime,CLR)是一个可由多种编程语言使用的“运行时”,CLR的核心功能(比如内存管理、程序集加载、安全性、异常处理和线程同步)可由面向CLR的所有语言使用。高级语言通常只公开了CLR的所有功能的一个子集。然而,IL汇编器语言允许开发人阅读全文
原文地址:https://www.cnblogs.com/Leo_wl/p/2432433.html