侧边栏壁纸
博主头像
Komi博主等级

WizMan Komi

  • 累计撰写 30 篇文章
  • 累计创建 43 个标签
  • 累计收到 3 条评论

目 录CONTENT

文章目录

初次使用Emit方法实现运行时生成IL代码

Komi
2022-09-21 / 0 评论 / 0 点赞 / 33 阅读 / 1,994 字
温馨提示:
内容仅供参考,实际使用需根据自身条件进行调整与删改

Emit方法是什么?

Contains classes that allow a compiler or tool to emit metadata and Microsoft intermediate language (MSIL) and optionally generate a PE file on disk. The primary clients of these classes are script engines and compilers.

这个方法存在于System.Reflection.Emit的namespace下,也就是反射中的一员,Emit方法能够让我们在程序的运行时获取对象的相关信息,创建对象的实例,执行方法

代码演示

让我们先来看一段普通的实体类代码以及它对应的IL程序
main.png

然后,我们这时可以借此机会利用Emit方法写一个基于泛型的setter方法

public static Action<T, object> EmitSetter<T>(string propertyName = "")
{
    var type = typeof(T);
    // 定义setter方法
    var dynamicMethod = new DynamicMethod("EmitCallable", null, new[]
    {
        type, typeof(object)
    }, type.Module);
    var iLGenerator = dynamicMethod.GetILGenerator();
    var callMethod = type.GetMethod("set_" + propertyName.ToLowerInvariant(), BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
    var parameterInfo = callMethod?.GetParameters()[0];
    if (parameterInfo is null) return dynamicMethod.CreateDelegate(typeof(Action<T, object>)) as Action<T, object>;
    var local
        = iLGenerator.DeclareLocal(parameterInfo.ParameterType, true
    #region EmitRegion
    iLGenerator.Emit(OpCodes.Ldarg_1);
    // 值类型->object拆箱操作
    // 引用类型->object需要Cast成对应的类
    iLGenerator.Emit(parameterInfo.ParameterType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, parameterInfo.ParameterType);
    iLGenerator.Emit(OpCodes.Stloc, local);
    iLGenerator.Emit(OpCodes.Ldarg_0);
    iLGenerator.Emit(OpCodes.Ldloc, local);
    iLGenerator.EmitCall(OpCodes.Callvirt, callMethod, null);
    iLGenerator.Emit(OpCodes.Ret);
    #endregion
    // 返回事件委托
    return dynamicMethod.CreateDelegate(typeof(Action<T, object>)) as Action<T, object>;
}

setter方法解读

  1. 方法的返回内容是一个Action,传入的参数则是T(泛型)以及一个值(setter值的传入),propertyName则为调用的setter字段
  2. 定义setter委托,DynamicMethod中,命名该方法为"EmitCallable",null则表明该方法不返回值,new[]{}数组代表着方法的参数类型,type.Module则是该方法关联的Module
  3. 定义一个ILGenerator并获取当前字段的setter方法
  4. 获取当前形参的ParameterInfo,若为null则直接返回空方法
  5. 根据parameterInfo定义一个LocalBuilder
  6. 进入Emit区域
  7. 先将索引1处的参数推送到计算堆栈上(setter的赋值),然后根据值的具体类型来进行拆箱操作或者转换类操作,再从堆栈中弹出一个值放到local这个局部变量上,将索引0处的自变量加载到计算堆栈上,然后需要将local这个局部变量加载到计算堆栈上,将对象绑定方法,并将返回值推送到计算堆栈上,最后从当前方法退出
  8. 退出Emit区域
  9. 返回当前的委托事件
0

评论区