如何快速编写和调试 Emit 生成 IL 的代码

本文介绍如何利用.NET的Emit命名空间在运行时动态生成IL代码,以替代反射并提升性能。通过ReSharper和dotPeek工具,可以加速IL的编写和调试过程。文中展示了动态设置未知类型属性值的例子,并详细解释了遇到的挑战,如类型转换问题,以及如何通过调试和修改Emit代码来解决问题。
摘要由CSDN通过智能技术生成

.NET Core/.NET Framework 的 System.Reflection.Emit 命名空间为我们提供了动态生成 IL 代码的能力。利用这项能力,我们能够在运行时生成一段代码/一个方法/一个类/一个程序集。

大家都知道反射的性能很差,通过缓存反射调用的方法则能够大幅提升性能。Emit 为我们提供了这项能力,我们能够在运行时生成一段代码,替代使用反射动态调用的代码,以提升性能。


我们在解决什么问题?

之前我写过一篇创建委托以大幅度提高反射调用的性能的方法,不过此方法适用于预先知道方法参数和返回值类型的情况。如果我们在编译期不知道类型,那么它就行不通了。(原因?注意到那篇文章中返回的委托有类型强转吗?也就是说需要编译期确定类型,即便是泛型。)

例如,我们在运行时得到一个对象,希望为这个对象的部分或全部属性赋值;此对象的类型和属性类型在编译期全部不可知(就算是泛型也没有)。

class SomeClass
{
    
    [DefaultValue("walterlv")]
    public string SomeProperty { get; set; }
}

众所周知的反射能够完成这个目标,但它不是本文讨论的重点;因为一旦这样的方法会被数万数十万甚至更多次调用的时候,反射将造成性能灾难。

既然反射不行,通过反射的创建委托也不行,那还有什么方法?

  1. 使用表达式树(不是本文重点)
  2. 使用 Emit(本文)

如果事先不能知道类型,那么只能每次通过反射去动态的调用,于是才会耗费大量的性能。如果我们能够在运行时动态地生成一段调用方法,那么这个调用方法将可以缓存下来供后续重复调用。如果我们使用 Emit,那么生成的方法与静态编写的代码是一样的,于是就能获得普通方法的性能。

为了实现动态地设置未知类型未知属性的值,我决定写出如下方法:

static void SetPropertyValue(object @this, object value)
{
    ((类的类型) @this).属性名称 = (属性的类型) value;
}

不用考虑编译问题了,这段代码是肯定编译不过的。方法是一个静态方法,传入两个参数——类型的实例和属性的新值;方法内部为实例中某个属性赋新值。

类的类型、属性名称和属性的类型是编译期不能确定,但可以在运行时确定的;如果此生成的方法会被大量调用,那么性能优势将极其明显。

快速编写 Emit

为了快速编写和调试 Emit,我们需要 ReSharper 全家桶:

  • ReSharper - 用于实时查看 IL 代码
  • dotPeek - 免费,用于查看我们使用 Emit 生成的代码,便于对比分析

相比于原生 Visual Studio,有此工具帮助的情况下,IL 的编写速度和调试速度将得到质的提升。(当然,利用这些工具依然只是手工操作,存在瓶颈;如果你阅读完本文之后找到或编写一个新的工具,更快,欢迎与我探讨。)

ReSharper 提供了 IL Viewer 窗格,从菜单依次进入 ReSharper->Windows->IL Viewer 可以打开。

ReSharper IL Viewer

打开后立即可以看到我们当前正在编写的代码的 IL,而且还能高亮光标所在的代码块。(如果你的 IL Viewer 中没有代码或没有高亮,编译一遍项目即可。)

IL Viewer 中的代码与编写的代码对应

我们要做的,就是得知 SetPropertyValue 在编译后将得到什么样的 IL 代码,这样我们才能编写出正确的 IL 生成代码来。于是编写这些辅助代码:

namespace Walterlv.Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            var instance = new TempClass();
            SetPropertyValue(instance, "test");
        }

        static void SetPropertyValue(object @this, object value)
        {
            ((TempClass) @this).TempProperty = (string) value;
        }
    }

    public cl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值