翼度科技»论坛 编程开发 .net 查看内容

Fireasy3 揭秘 -- 使用 Emit 构建程序集

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
目录


  • Fireasy3 揭秘 -- 依赖注入与服务发现
  • Fireasy3 揭秘 -- 自动服务部署
  • Fireasy3 揭秘 -- 使用 SourceGeneraor 改进服务发现
  • Fireasy3 揭秘 -- 使用 SourceGeneraor 实现动态代理(AOP)
  • Fireasy3 揭秘 -- 使用 Emit 构建程序集
  • Fireasy3 揭秘 -- 使用缓存提高反射性能
  • Fireasy3 揭秘 -- 动态类型及扩展支持
  • Fireasy3 揭秘 -- 线程数据共享的实现
  • Fireasy3 揭秘 -- 配置管理及解析处理
  • Fireasy3 揭秘 -- 数据库适配器
  • Fireasy3 揭秘 -- 解决数据库之间的语法差异
  • Fireasy3 揭秘 -- 获取数据库的架构信息
  • Fireasy3 揭秘 -- 数据批量插入的实现
  • Fireasy3 揭秘 -- 使用包装器对数据读取进行兼容
  • Fireasy3 揭秘 -- 数据行映射器
  • Fireasy3 揭秘 -- 数据转换器的实现
  • Fireasy3 揭秘 -- 通用序列生成器和雪花生成器的实现
  • Fireasy3 揭秘 -- 命令拦截器的实现
  • Fireasy3 揭秘 -- 数据库主从同步的实现
  • Fireasy3 揭秘 -- 大数据分页的策略
  • Fireasy3 揭秘 -- 数据按需更新及生成实体代理类
  • Fireasy3 揭秘 -- 用对象池技术管理上下文
  • Fireasy3 揭秘 -- Lambda 表达式解析的原理
  • Fireasy3 揭秘 -- 扩展选择的实现
  • Fireasy3 揭秘 -- 按需加载与惰性加载的区别与实现
  • Fireasy3 揭秘 -- 自定义函数的解析与绑定
  • Fireasy3 揭秘 -- 与 MongoDB 进行适配
  • Fireasy3 揭秘 -- 模块化的实现原理
  在运行期间,我们可以使用 Emit 来组织一段 IL 代码,进而动态生成一个方法,甚至是一个程序集(包括类型、方法或属性等等)。这个过程我们称之为动态编织。这一项技术应用比较广泛,比如数据映射(Dapper)、动态代理(AOP)等等,目的是提升大量反射而产生的性能问题。
  在 Fireasy 里,提供了以下几个构造器,用于生成一个完整的程序集:

  • DynamicAssemblyBuilder 动态程序集构造器
  • DynamicTypeBuilder 动态类型构造器
  • DynamicInterfaceBuilder 动态接口构造器
  • DynamicEnumBuilder 动态枚举构造器
  • DynamicFieldBuilder 动态字段域构造器
  • DynamicPropertyBuilder 动态属性构造器
  • DynamicConstructorBuilder 动态构造函数构造器
  • DynamicMethodBuilder 动态方法构造器
  接下来,我会对每个构造器进行介绍,以了解它们的基本原理。
DynamicAssemblyBuilder

  动态程序集构造器是一个起点,你只有先创建了一个程序集,才能在程序集里定义各种接口、类型。
  其实它的核心是在当前程序域内定义一个 AssemblyBuilder,再此基础上再定义一个 ModuleBuilder,这是程序集内部的结构,它们都来自于 System.Reflection.Emit 命名空间下。如下:
  1.     public class DynamicAssemblyBuilder : DynamicBuilder
  2.     {
  3.         private AssemblyBuilder _assemblyBuilder;
  4.         private ModuleBuilder _moduleBuilder;
  5.         private AssemblyBuilder InitAssemblyBuilder()
  6.         {
  7.             if (_assemblyBuilder == null)
  8.             {
  9.                 var an = new AssemblyName(AssemblyName);
  10.                 if (string.IsNullOrEmpty(OutputAssembly))
  11.                 {
  12. #if NETFRAMEWORK
  13.                     _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
  14. #else
  15.                     _assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndCollect);
  16. #endif
  17.                 }
  18.                 else
  19.                 {
  20. #if NETFRAMEWORK
  21.                     var dir = Path.GetDirectoryName(OutputAssembly);
  22.                     if (!Directory.Exists(dir))
  23.                     {
  24.                         Directory.CreateDirectory(dir);
  25.                     }
  26.                     _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave, dir);
  27. #else
  28.                     _assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
  29. #endif
  30.                 }
  31.             }
  32.             return _assemblyBuilder;
  33.         }
  34.         /// <summary>
  35.         /// 获取 <see cref="ModuleBuilder"/> 对象。
  36.         /// </summary>
  37.         /// <returns></returns>
  38.         private ModuleBuilder InitModuleBuilder()
  39.         {
  40.             if (_moduleBuilder == null)
  41.             {
  42.                 if (string.IsNullOrEmpty(OutputAssembly))
  43.                 {
  44.                     _moduleBuilder = AssemblyBuilder.DefineDynamicModule("Main");
  45.                 }
  46.                 else
  47.                 {
  48.                     var fileName = OutputAssembly.Substring(OutputAssembly.LastIndexOf("\") + 1);
  49. #if NETFRAMEWORK
  50.                     _moduleBuilder = AssemblyBuilder.DefineDynamicModule(fileName, fileName);
  51. #else
  52.                     _moduleBuilder = AssemblyBuilder.DefineDynamicModule(fileName);
  53. #endif
  54.                 }
  55.             }
  56.             return _moduleBuilder;
  57.         }
  58.     }
复制代码
  在 .net framework 时代,我们可以将动态构造的程序集存储到磁盘中,很遗憾,从 .net standard 之后就无法实现这一功能了,这给我们带来了诸多不便,因为动态生成的程序集必须存储在内存当中了。
  然后,DynamicAssemblyBuilder 提供了定义接口、类型和枚举的方法,将工作交给这些不同的构造器。如下:
  1.         /// <summary>
  2.         /// 使用当前的构造器定义一个动态类型。
  3.         /// </summary>
  4.         /// <param name="typeName">类型的名称。</param>
  5.         /// <param name="accessibility">指定类的可见性。</param>
  6.         /// <param name="modifier">指定类的调用属性。</param>
  7.         /// <param name="baseType">类型的父类。</param>
  8.         /// <returns></returns>
  9.         public DynamicTypeBuilder DefineType(string typeName, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Type baseType = null)
  10.         {
  11.             var typeBuilder = new DynamicTypeBuilder(Context, typeName, accessibility, modifier, baseType);
  12.             _typeBuilders.Add(typeBuilder);
  13.             return typeBuilder;
  14.         }
  15.         /// <summary>
  16.         /// 使用当前的构造器定义一个动态接口。
  17.         /// </summary>
  18.         /// <param name="typeName">类型的名称。</param>
  19.         /// <param name="accessibility">指定类的可见性。</param>
  20.         /// <returns></returns>
  21.         public DynamicInterfaceBuilder DefineInterface(string typeName, Accessibility accessibility = Accessibility.Public)
  22.         {
  23.             var typeBuilder = new DynamicInterfaceBuilder(Context, typeName, accessibility);
  24.             _typeBuilders.Add(typeBuilder);
  25.             return typeBuilder;
  26.         }
  27.         /// <summary>
  28.         /// 使用当前构造器定义一个枚举。
  29.         /// </summary>
  30.         /// <param name="enumName">枚举的名称。</param>
  31.         /// <param name="underlyingType">枚举的类型。</param>
  32.         /// <param name="accessibility">指定枚举的可见性。</param>
  33.         /// <returns></returns>
  34.         public DynamicEnumBuilder DefineEnum(string enumName, Type? underlyingType = null, Accessibility accessibility = Accessibility.Public)
  35.         {
  36.             var enumBuilder = new DynamicEnumBuilder(Context, enumName, underlyingType ?? typeof(int), accessibility);
  37.             _typeBuilders.Add(enumBuilder);
  38.             return enumBuilder;
  39.         }
复制代码
  所以 DynamicAssemblyBuilder 最多只能算是一个容器,更多的工作分解到各种构造器中。上面的方法中,定义接口、类型、枚举时,都将相应的构造器放到 _typeBuilders 集合里,在最后调用 Create 方法逐一创建。如下:
  1.         /// <summary>
  2.         /// 创建程序集。
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         public Assembly Create()
  6.         {
  7.             if (!_isCreated)
  8.             {
  9.                 foreach (var typeBuilder in _typeBuilders)
  10.                 {
  11.                     typeBuilder.CreateType();
  12.                 }
  13.                 _isCreated = true;
  14.             }
  15.             return AssemblyBuilder;
  16.         }
复制代码
DynamicTypeBuilder

  动态类型构造器是对 TypeBuilder 的包装。这里需要强调的是,在 DefineType 时,类的访问性由 Accessibility 枚举来指定,如 public、private、protected、internal 等等,修饰性则由 Modifier 枚举来指定,如 abstract、sealed。以下的方法主要用来设定类型的的 TypeAttributes,如下:
  1.         private TypeAttributes GetTypeAttributes(Accessibility accessibility, Modifier modifier)
  2.         {
  3.             var attrs = GetTypeAttributes();
  4.             switch (modifier)
  5.             {
  6.                 case Modifier.Abstract:
  7.                     attrs |= TypeAttributes.Abstract;
  8.                     break;
  9.                 case Modifier.Sealed:
  10.                     attrs |= TypeAttributes.Sealed;
  11.                     break;
  12.             }
  13.             switch (accessibility)
  14.             {
  15.                 case Accessibility.Internal:
  16.                     if (_isNesetType)
  17.                     {
  18.                         attrs |= TypeAttributes.NestedAssembly;
  19.                     }
  20.                     break;
  21.                 case Accessibility.Private:
  22.                     if (_isNesetType)
  23.                     {
  24.                         attrs |= TypeAttributes.NestedPrivate;
  25.                     }
  26.                     break;
  27.                 case Accessibility.Public:
  28.                     attrs |= _isNesetType ? TypeAttributes.NestedPublic : TypeAttributes.Public;
  29.                     break;
  30.             }
  31.             return attrs;
  32.         }
复制代码
  这里重点要介绍一下泛型类型参数的处理。为了兼容定义方法时的泛型类型参数,这里定义了一个 GtpType 类型(Generic Type Parameter),它继承自 Type 类型,主要用到 Name 属性,其他属性均忽略。另外,通过以下的方法设定约束:
  1.     /// <summary>
  2.     /// 用于标识一个泛型类型参数的类型。无法继承此类。
  3.     /// </summary>
  4.     public sealed class GtpType : Type
  5.     {
  6.         private Type? _baseType;
  7.         private Type[]? _constraintTypes;
  8.         private GenericParameterAttributes? _parameterAttributes;
  9.         /// <summary>
  10.         /// 设置基类约束。
  11.         /// </summary>
  12.         /// <param name="baseType">基类类型。</param>
  13.         /// <returns></returns>
  14.         public GtpType SetBaseTypeConstraint(Type baseType)
  15.         {
  16.             _baseType = baseType;
  17.             return this;
  18.         }
  19.         /// <summary>
  20.         /// 设置接口约束。
  21.         /// </summary>
  22.         /// <param name="constraintTypes">约束类型。</param>
  23.         /// <returns></returns>
  24.         public GtpType SetInterfaceConstraints(params Type[] constraintTypes)
  25.         {
  26.             _constraintTypes = constraintTypes;
  27.             return this;
  28.         }
  29.         /// <summary>
  30.         /// 设置参数特性。
  31.         /// </summary>
  32.         /// <param name="attributes"></param>
  33.         /// <returns></returns>
  34.         public GtpType SetGenericParameterAttributes(GenericParameterAttributes attributes)
  35.         {
  36.             _parameterAttributes = attributes;
  37.             return this;
  38.         }
  39.     }
复制代码
TypeBuilder 提供了一个方法 DefineGenericParameters 用于定义泛型类型参数。GtpType 的 Initialize 方法是将基类约束、接口约束等设定到 GenericTypeParameterBuilder 对象里。如下:
  1.         /// <summary>
  2.         /// 定义泛型参数。
  3.         /// </summary>
  4.         /// <param name="parameterTypes"></param>
  5.         /// <returns></returns>
  6.         public void DefineGenericParameters(params GtpType[] parameterTypes)
  7.         {
  8.             if (_genericParameterTypes != null)
  9.             {
  10.                 throw new InvalidOperationException("已经定义过泛型参数。");
  11.             }
  12.             _genericParameterTypes = parameterTypes.ToDictionary(s => s.Name);
  13.             var builders = _typeBuilder.DefineGenericParameters(parameterTypes.Select(s => s.Name).ToArray());
  14.             for (var i = 0; i < parameterTypes.Length; i++)
  15.             {
  16.                 parameterTypes[i].Initialize(builders[i]);
  17.             }
  18.         }
复制代码
  然后,DynamicTypeBuilder 提供了定义方法、构造函数、属性和嵌套类型的方法,将工作交给这些不同的构造器。如下:
  1.         /// <summary>
  2.         /// 定义一个属性。
  3.         /// </summary>
  4.         /// <param name="propertyName">属性的名称。</param>
  5.         /// <param name="propertyType">属性的类型。</param>
  6.         /// <param name="accessibility">指定属性的可见性。</param>
  7.         /// <param name="modifier">指定属性的调用属性。</param>
  8.         /// <returns>新的 <see cref="DynamicPropertyBuilder"/>。</returns>
  9.         public virtual DynamicPropertyBuilder DefineProperty(string propertyName, Type propertyType, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard)
  10.         {
  11.             return new DynamicPropertyBuilder(Context, propertyName, propertyType, accessibility, modifier);
  12.         }
  13.         /// <summary>
  14.         /// 定义一个方法。
  15.         /// </summary>
  16.         /// <param name="methodName">方法的名称。</param>
  17.         /// <param name="returnType">返回值的类型,如果为 void 则该参数为 null。</param>
  18.         /// <param name="parameterTypes">一个数组,表示方法的传入参数类型。</param>
  19.         /// <param name="accessibility">指定方法的可见性。</param>
  20.         /// <param name="modifier">指定方法的调用属性。</param>
  21.         /// <param name="ilCoding">方法体的 IL 过程。</param>
  22.         /// <returns>新的 <see cref="DynamicMethodBuilder"/>。</returns>
  23.         public virtual DynamicMethodBuilder DefineMethod(string methodName, Type? returnType = null, Type[]? parameterTypes = null, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
  24.         {
  25.             return new DynamicMethodBuilder(Context, methodName, returnType, parameterTypes, accessibility, modifier, ilCoding);
  26.         }
  27.         /// <summary>
  28.         /// 定义一个构造函数。
  29.         /// </summary>
  30.         /// <param name="parameterTypes"></param>
  31.         /// <param name="accessibility"></param>
  32.         /// <param name="modifier"></param>
  33.         /// <param name="ilCoding"></param>
  34.         /// <returns></returns>
  35.         public virtual DynamicConstructorBuilder DefineConstructor(Type[] parameterTypes, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
  36.         {
  37.             return new DynamicConstructorBuilder(Context, parameterTypes, accessibility, modifier, ilCoding);
  38.         }
  39.         /// <summary>
  40.         /// 定义一个字段。
  41.         /// </summary>
  42.         /// <param name="fieldName">字段的名称。</param>
  43.         /// <param name="fieldType">字段的类型。</param>
  44.         /// <param name="defaultValue">默认值。</param>
  45.         /// <param name="accessibility"></param>
  46.         /// <param name="modifier"></param>
  47.         /// <returns></returns>
  48.         public virtual DynamicFieldBuilder DefineField(string fieldName, Type fieldType, object? defaultValue = null, Accessibility accessibility = Accessibility.Private, Modifier modifier = Modifier.Standard)
  49.         {
  50.             return new DynamicFieldBuilder(Context, fieldName, fieldType, defaultValue, accessibility, modifier);
  51.         }
  52.         /// <summary>
  53.         /// 定义一个嵌套的类型。
  54.         /// </summary>
  55.         /// <param name="typeName"></param>
  56.         /// <param name="accessibility"></param>
  57.         /// <param name="baseType"></param>
  58.         /// <returns></returns>
  59.         public virtual DynamicTypeBuilder DefineNestedType(string typeName, Accessibility accessibility = Accessibility.Private, Type? baseType = null)
  60.         {
  61.             var nestedType = new DynamicTypeBuilder(Context, typeName, accessibility, baseType);
  62.             _nestedTypeBuilders.Add(nestedType);
  63.             return nestedType;
  64.         }
  65.         /// <summary>
  66.         /// 使用当前的构造器定义一个动态接口。
  67.         /// </summary>
  68.         /// <param name="typeName">类型的名称。</param>
  69.         /// <param name="accessibility">指定类的可见性。</param>
  70.         /// <returns></returns>
  71.         public DynamicInterfaceBuilder DefineNestedInterface(string typeName, Accessibility accessibility = Accessibility.Public)
  72.         {
  73.             var typeBuilder = new DynamicInterfaceBuilder(Context, typeName, accessibility);
  74.             _nestedTypeBuilders.Add(typeBuilder);
  75.             return typeBuilder;
  76.         }
  77.         /// <summary>
  78.         /// 使用当前构造器定义一个枚举。
  79.         /// </summary>
  80.         /// <param name="enumName">枚举的名称。</param>
  81.         /// <param name="underlyingType">枚举的类型。</param>
  82.         /// <param name="accessibility">指定枚举的可见性。</param>
  83.         /// <returns></returns>
  84.         public DynamicEnumBuilder DefineNestedEnum(string enumName, Type? underlyingType = null, Accessibility accessibility = Accessibility.Public)
  85.         {
  86.             var enumBuilder = new DynamicEnumBuilder(Context, enumName, underlyingType ?? typeof(int), accessibility);
  87.             _nestedTypeBuilders.Add(enumBuilder);
  88.             return enumBuilder;
  89.         }
复制代码
DynamicMethodBuilder

  动态方法构造器在定义时,需要指定参数类型,返回类型等等,对于泛型方法,也需要使用 GtpType 类型来定义。以下的方法是用于处理泛型方法的:
  1.         private void ProcessGenericMethod()
  2.         {
  3.             Dictionary<string, GenericTypeParameterBuilder>? builders = null;
  4.             //方法参数里有泛型类型参数
  5.             if (ParameterTypes?.Any(s => s is GtpType) == true)
  6.             {
  7.                 //筛选没有在类型构造器里定义过的泛型类型参数
  8.                 var names = ParameterTypes.Where(s => s is GtpType).Where(s => !Context.TypeBuilder.TryGetGenericParameterType(s.Name, out _)).Cast<GtpType>().Select(s => s.Name).ToArray();
  9.                 //如果有新的泛型类型参数,则在方法构造器里定义
  10.                 if (names.Length > 0)
  11.                 {
  12.                      builders = _methodBuilder.DefineGenericParameters(names).ToDictionary(s => s.Name);
  13.                 }
  14.                 for (var i = 0; i < ParameterTypes.Length; i++)
  15.                 {
  16.                     if (ParameterTypes[i] is GtpType gt)
  17.                     {
  18.                         if (builders?.TryGetValue(gt.Name, out var parb) == true)
  19.                         {
  20.                             ParameterTypes[i] = gt.Initialize(parb);
  21.                         }
  22.                         else if (Context.TypeBuilder.TryGetGenericParameterType(gt.Name, out var gt1))
  23.                         {
  24.                             ParameterTypes[i] = gt1.GenericTypeParameterBuilder;
  25.                         }
  26.                     }
  27.                 }
  28.                 MethodBuilder.SetParameters(ParameterTypes);
  29.             }
  30.             //如果返回类型是泛型类型
  31.             if (ReturnType is GtpType rgt)
  32.             {
  33.                 //先在方法构造器里查找
  34.                 if (builders?.TryGetValue(rgt.Name, out var retb) == true)
  35.                 {
  36.                     ReturnType = rgt.Initialize(retb);
  37.                 }
  38.                 //在类型构造器里查找
  39.                 else if (Context.TypeBuilder.TryGetGenericParameterType(rgt.Name, out var gt1))
  40.                 {
  41.                     ReturnType = gt1.GenericTypeParameterBuilder;
  42.                 }
  43.             }
  44.         }
复制代码
  这样,就完美处理了泛型方法,文章最后会例举测试用例进一步加深印象。
  如果动态类型指定了所继承的基类,还需要处理重载方法,以下的方法用于在父类中查找与名称和参数相匹配的方法:
  1.         private MethodInfo? FindMethod(string methodName, IEnumerable<Type> parameterTypes)
  2.         {
  3.             MethodInfo? method = null;
  4.             if (Context.TypeBuilder.BaseType != null)
  5.             {
  6.                 method = Helper.MatchMethod(Context.TypeBuilder.BaseType, methodName, parameterTypes);
  7.                 if (method != null && !method.IsVirtual)
  8.                 {
  9.                     throw new DynamicBuildException("所定义的方法在父类中未标记 virtual、abstract 或 override。");
  10.                 }
  11.             }
  12.             //在实现的接口中查找方法
  13.             var interfaceTypes = Context.TypeBuilder.InterfaceTypes
  14.                 .Union(Context.TypeBuilder.InterfaceTypes.SelectMany(s => s.GetInterfaces()))
  15.                 .Distinct().ToList();
  16.             //在实现接口中去查找方法
  17.             if (method == null && interfaceTypes.Count != 0)
  18.             {
  19.                 foreach (var type in interfaceTypes)
  20.                 {
  21.                     method = type.GetMethod(methodName, parameterTypes == null ? Type.EmptyTypes : parameterTypes.ToArray());
  22.                     if (method != null)
  23.                     {
  24.                         break;
  25.                     }
  26.                 }
  27.             }
  28.             return method;
  29.         }
复制代码
  在处理 MethodAttributes 时,如果匹配到父类的方法,则也会有不同的处理,这些属性的含义,需要自己去慢慢理解。如下:
  1.         private MethodAttributes GetMethodAttributes(string methodName, IEnumerable<Type> parameterTypes, Accessibility accessibility, Modifier modifier)
  2.         {
  3.             var method = FindMethod(methodName, parameterTypes);
  4.             var isOverride = method != null && method.IsVirtual;
  5.             var isInterface = isOverride && method!.DeclaringType!.IsInterface;
  6.             var isBaseType = isOverride && method!.DeclaringType == Context.TypeBuilder.BaseType;
  7.             if (method != null)
  8.             {
  9.                 Context.BaseMethod = method;
  10.             }
  11.             var attrs = GetMethodAttributes(accessibility, modifier);
  12.             if (isOverride)
  13.             {
  14.                 attrs |= MethodAttributes.Virtual;
  15.                 //去掉 NewSlot
  16.                 if (isBaseType && _attributes.HasFlag(MethodAttributes.NewSlot))
  17.                 {
  18.                     attrs &= ~MethodAttributes.NewSlot;
  19.                 }
  20.                 else if (isInterface)
  21.                 {
  22.                     //如果没有传入 modifier,则加 Final 去除上面定义的 Virtual
  23.                     if (modifier == Modifier.Standard)
  24.                     {
  25.                         attrs |= MethodAttributes.Final;
  26.                     }
  27.                     attrs |= MethodAttributes.NewSlot;
  28.                 }
  29.             }
  30.             else if (method != null)
  31.             {
  32.             }
  33.             return attrs;
  34.         }
复制代码
  在 DefineMethod 方法中,最后一个参数 ilCoding 允许你用 IL 指令编写一段代码,以作为方法体。Emitter 属性是一个 EmitHelper 对象,它是对 ILGenerator 对象的包装,可以更方便地使用链式语法编写 IL 指令。从构造函数里调用 InitBuilder 方法可以看出它的工作原理:
  1.         private void InitBuilder()
  2.         {
  3.             //此处略去
  4.             Context.Emitter = new EmitHelper(_methodBuilder.GetILGenerator(), _methodBuilder);
  5.             //此处略去
  6.             if (_buildAction != null)
  7.             {
  8.                 _buildAction(Context);
  9.             }
  10.             else
  11.             {
  12.                 Context.Emitter.ret();
  13.             }
  14.         }
复制代码
  通过 DefineMethod 方法编写 IL 指令后,还可以使用 OverwriteCode 方法进行覆盖,或使用 AppendCode 方法进行追加,如下:
  1.         /// <summary>
  2.         /// 追加新的 MSIL 代码到构造器中。
  3.         /// </summary>
  4.         /// <param name="ilCoding"></param>
  5.         /// <returns></returns>
  6.         public DynamicMethodBuilder AppendCode(Action<EmitHelper> ilCoding)
  7.         {
  8.             ilCoding?.Invoke(Context.Emitter);
  9.             return this;
  10.         }
  11.         /// <summary>
  12.         /// 使用新的 MSIL 代码覆盖构造器中的现有代码。
  13.         /// </summary>
  14.         /// <param name="ilCoding"></param>
  15.         /// <returns></returns>
  16.         public DynamicMethodBuilder OverwriteCode(Action<EmitHelper> ilCoding)
  17.         {
  18.             var field = typeof(MethodBuilder).GetField("m_ilGenerator", BindingFlags.NonPublic | BindingFlags.Instance);
  19.             if (field != null)
  20.             {
  21.                 var cons = typeof(ILGenerator).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
  22.                 field.SetValue(_methodBuilder, cons.Invoke(new[] { _methodBuilder }));
  23.                 Context.Emitter = new EmitHelper(_methodBuilder.GetILGenerator(), _methodBuilder);
  24.             }
  25.             return AppendCode(ilCoding);
  26.         }
复制代码
DynamicPropertyBuilder

  动态属性构造器就要容易一些,只需要用它来定义 get 和 set 方法,它一般是自动属性的,如果要使用到字段域,则传入字段域构造器即可。如果你的 get 或 set 方法很复杂,那就使用 ilCoding 进行指令编码。如下:
  1.         /// <summary>
  2.         /// 获取当前的 <see cref="DynamicFieldBuilder"/>。
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         public DynamicFieldBuilder FieldBuilder
  6.         {
  7.             get
  8.             {
  9.                 return _fieldBuilder ?? (_fieldBuilder = Context.TypeBuilder.DefineField(string.Format("m_<{0}>", Name), PropertyType));
  10.             }
  11.         }
  12.         /// <summary>
  13.         /// 定义属性的 Get 访问方法。
  14.         /// </summary>
  15.         /// <param name="accessibility">指定方法的可见性。</param>
  16.         /// <param name="modifier">指定方法的调用属性。</param>
  17.         /// <param name="ilCoding">方法体的 IL 过程。</param>
  18.         /// <returns>新的 <see cref="DynamicMethodBuilder"/>。</returns>
  19.         public DynamicMethodBuilder DefineGetMethod(Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
  20.         {
  21.             var isInterface = Context.TypeBuilder is DynamicInterfaceBuilder;
  22.             var method = new DynamicMethodBuilder(Context, string.Concat("get_", GetMethodName()), PropertyType, Type.EmptyTypes, accessibility, modifier, ctx =>
  23.                 {
  24.                     if (isInterface)
  25.                     {
  26.                         return;
  27.                     }
  28.                     if (ilCoding != null)
  29.                     {
  30.                         ilCoding(ctx);
  31.                     }
  32.                     else
  33.                     {
  34.                         ctx.Emitter.ldarg_0.ldfld(FieldBuilder.FieldBuilder).ret();
  35.                     }
  36.                 });
  37.             PropertyBuilder.SetGetMethod(method.MethodBuilder);
  38.             return method;
  39.         }
  40.         /// <summary>
  41.         /// 定义属性的 Get 访问方法。
  42.         /// </summary>
  43.         /// <param name="accessibility">指定方法的可见性。</param>
  44.         /// <param name="modifier">指定方法的调用属性。</param>
  45.         /// <param name="fieldBuilder">指定一个属性相关的 <see cref="DynamicFieldBuilder"/>。</param>
  46.         /// <returns>新的 <see cref="DynamicMethodBuilder"/>。</returns>
  47.         public DynamicMethodBuilder DefineGetMethodByField(Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, DynamicFieldBuilder? fieldBuilder = null)
  48.         {
  49.             var isInterface = Context.TypeBuilder is DynamicInterfaceBuilder;
  50.             var method = new DynamicMethodBuilder(Context, string.Concat("get_", GetMethodName()), PropertyType, Type.EmptyTypes, accessibility, modifier, ctx =>
  51.                 {
  52.                     if (isInterface)
  53.                     {
  54.                         return;
  55.                     }
  56.                     fieldBuilder ??= FieldBuilder;
  57.                     ctx.Emitter.ldarg_0.ldfld(fieldBuilder.FieldBuilder).ret();
  58.                 });
  59.             PropertyBuilder.SetGetMethod(method.MethodBuilder);
  60.             return method;
  61.         }
复制代码
DynamicConstructorBuilder

动态构造函数构造器,如果类型继承了基类,则默认的方法体需要调用父类构造函数。如下:
  1.         internal DynamicConstructorBuilder(BuildContext context, Type[] parameterTypes, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
  2.             : base(accessibility, modifier)
  3.         {
  4.             Context = new BuildContext(context) { ConstructorBuilder = this };
  5.             ParameterTypes = parameterTypes;
  6.             if (ilCoding == null)
  7.             {
  8.                 if (context.TypeBuilder.BaseType != typeof(object))
  9.                 {
  10.                     var constructor = Helper.MatchConstructor(Context.TypeBuilder.BaseType, parameterTypes);
  11.                     if (constructor != null)
  12.                     {
  13.                         ilCoding = c => c.Emitter.ldarg_0
  14.                             .If(parameterTypes != null, b => b.For(0, parameterTypes!.Length, (e, i) => e.ldarg(i + 1)))
  15.                             .call(constructor).ret();
  16.                     }
  17.                 }
  18.             }
  19.             ilCoding ??= c => c.Emitter.ret();
  20.             _buildAction = ilCoding;
  21.             _attributes = GetMethodAttributes(accessibility, modifier);
  22.             InitBuilder();
  23.         }
复制代码
  最后,说一下关于自定义特性。在构造器基类 DynamicBuilder 里,有一个 SetCustomAttribute 方法,它可以使用 lambda 表达式来指定自定义特性。
测试用例

  创建一个类型,继承基类,实现接口:
  1.         [TestMethod]
  2.         public void TestTypeBuilder()
  3.         {
  4.             /*
  5.             public class MyClass : MyBaseClass, IMyInterface
  6.             {
  7.                 public string Title { get; set; }
  8.                 public void HelloWorld()
  9.                 {
  10.                 }
  11.                 public void WriteName(string a1, string a2)
  12.                 {
  13.                 }
  14.             }
  15.             */
  16.             var assemblyBuilder = new DynamicAssemblyBuilder("MyAssembly");
  17.             var typeBuilder = assemblyBuilder.DefineType("MyClass");
  18.             typeBuilder.BaseType = typeof(MyBaseClass);
  19.             typeBuilder.ImplementInterface(typeof(IMyInterface));
  20.             var methodBuilder = typeBuilder.DefineMethod("HelloWorld");
  21.             var propertyBuilder = typeBuilder.DefineProperty("Title", typeof(string)).DefineGetSetMethods();
  22.             methodBuilder = typeBuilder.DefineMethod("WriteName", typeof(string), new[] { typeof(string) });
  23.             var type = typeBuilder.CreateType();
  24.             Assert.IsTrue(typeof(IMyInterface).IsAssignableFrom(type));
  25.         }
复制代码
  创建一个泛型类型,以及泛型方法:
  1.         [TestMethod]
  2.         public void TestDefineGenericType()
  3.         {
  4.             /*
  5.             public class MyClass<T, TS> where T : MyBaseClass
  6.             {
  7.                 public MyClass(TS ts)
  8.                 {
  9.                 }
  10.                 public T Hello<TV>(T t, TV tv)
  11.                 {
  12.                     Console.WriteLine(tv);
  13.                     return t;
  14.                 }
  15.             }
  16.             */
  17.             var gt = new GtpType("T").SetBaseTypeConstraint(typeof(MyBaseClass));
  18.             var assemblyBuilder = new DynamicAssemblyBuilder("MyAssembly");
  19.             var typeBuilder = assemblyBuilder.DefineType("MyClass");
  20.             //定义泛型类型参数
  21.             typeBuilder.DefineGenericParameters(gt, new GtpType("TS"));
  22.             //定义构造函数
  23.             typeBuilder.DefineConstructor(new Type[] { new GtpType("TS") });
  24.             //定义一个泛型方法,TV不在类中定义,所以属于方法的泛型类型参数
  25.             var methodBuilder = typeBuilder.DefineMethod("Hello", gt, new Type[] { gt, new GtpType("TV") }, ilCoding: c =>
  26.             {
  27.                 c.Emitter
  28.                 .ldarg_2.call(typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }))
  29.                 .ldarg_1.ret();
  30.             });
  31.             var type = typeBuilder.CreateType().MakeGenericType(typeof(MyBaseClass), typeof(int));
  32.             var obj = Activator.CreateInstance(type, 100);
  33.             var method = type.GetMethod("Hello").MakeGenericMethod(typeof(string));
  34.             var value = method.Invoke(obj, new object[] { new MyBaseClass(), "world" });
  35.             Assert.IsInstanceOfType(value, typeof(MyBaseClass));
  36.         }
复制代码
  定义泛型方法:
  1.         /// <summary>
  2.         /// 使用泛型参数测试DefineMethod方法。
  3.         /// </summary>
  4.         [TestMethod()]
  5.         public void TestDefineGenericMethod()
  6.         {
  7.             var typeBuilder = CreateBuilder();
  8.             /*
  9.             public class testClass
  10.             {
  11.                 public void Hello<T1, T2>(string name, T1 any1, T2 any2)
  12.                 {
  13.                     Console.Write(name + any1 + any2);
  14.                 }
  15.             }
  16.             */
  17.             var methodBuilder = typeBuilder.DefineMethod("Hello", parameterTypes: new Type[] { typeof(string), new GtpType("T1"), new GtpType("T2") });
  18.             methodBuilder.DefineParameter("name");
  19.             methodBuilder.DefineParameter("any1");
  20.             methodBuilder.DefineParameter("any2");
  21.             var paraCount = methodBuilder.ParameterTypes.Length;
  22.             methodBuilder.OverwriteCode(e =>
  23.             {
  24.                 e.ldc_i4(paraCount)
  25.                 .newarr(typeof(object))
  26.                 .dup.ldc_i4_0.ldarg_1.stelem_ref
  27.                 .For(1, paraCount, (e1, i) =>
  28.                 {
  29.                     e1.dup.ldc_i4(i).ldarg(i + 1).box(methodBuilder.ParameterTypes[i]).stelem_ref.end();
  30.                 })
  31.                 .call(typeof(string).GetMethod("Concat", new[] { typeof(object[]) }))
  32.                 .call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }))
  33.                 .ret();
  34.             });
  35.             var type = typeBuilder.CreateType();
  36.             var method = type.GetMethod("Hello");
  37.             Assert.IsNotNull(method);
  38.             Assert.IsTrue(method.IsGenericMethod);
  39.             var obj = Activator.CreateInstance(type);
  40.             method = method.MakeGenericMethod(typeof(int), typeof(decimal));
  41.             method.Invoke(obj, new object[] { "fireasy", 22, 45m });
  42.         }
复制代码
  显式实现方法:
  1.         /// <summary>
  2.         /// 使用接口成员显式实现测试ImplementInterface方法。
  3.         /// </summary>
  4.         [TestMethod()]
  5.         public void ImplementInterfaceWithExplicitMember()
  6.         {
  7.             /*
  8.             public class testClass : IDynamicMethodInterface
  9.             {
  10.                 void IDynamicMethodInterface.Test(int s)
  11.                 {
  12.                     Console.WriteLine(s);
  13.                 }
  14.             }
  15.             */
  16.             var typeBuilder = CreateBuilder();
  17.             typeBuilder.ImplementInterface(typeof(IDynamicMethodInterface));
  18.             var methodBuilder = typeBuilder.DefineMethod("Test",
  19.                 parameterTypes: new[] { typeof(int) },
  20.                 modifier: Modifier.ExplicitImpl,
  21.                 ilCoding: (e) => e.Emitter.ldstr("fireasy").call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })).ret());
  22.             methodBuilder.DefineParameter("s");
  23.             var type = typeBuilder.CreateType();
  24.             var obj = Activator.CreateInstance(type) as IDynamicMethodInterface;
  25.             obj.Test(111);
  26.             Assert.IsTrue(typeof(IDynamicMethodInterface).IsAssignableFrom(type));
  27.         }
复制代码
  继承泛型基类,并且定义构造函数:
  1.         /// <summary>
  2.         /// 测试DefineConstructor方法。
  3.         /// </summary>
  4.         [TestMethod()]
  5.         public void TestDefineConstructorForGeneric()
  6.         {
  7.             var typeBuilder = CreateBuilder();
  8.             /*
  9.             public class testClass<T> : GenericClass<T>
  10.             {
  11.                 public testClass(T value)
  12.                     : base (value)
  13.                 {
  14.                 }
  15.             }
  16.             */
  17.             var gtp = new GtpType("T");
  18.             typeBuilder.BaseType = typeof(GenericClass<>);
  19.             typeBuilder.DefineGenericParameters(gtp);
  20.             var constructorBuilder = typeBuilder.DefineConstructor(new Type[] { gtp });
  21.             constructorBuilder.DefineParameter("value");
  22.             var type = typeBuilder.CreateType();
  23.             type = type.MakeGenericType(typeof(string));
  24.             var obj = Activator.CreateInstance(type, new[] { "fireasy" });
  25.             Assert.IsNotNull(obj);
  26.         }
复制代码
  最后,奉上 Fireasy 3 的开源地址:https://gitee.com/faib920/fireasy3 ,欢迎大家前来捧场。
  本文相关代码请参考:
  https://gitee.com/faib920/fireasy3/src/libraries/Fireasy.Common/Emit
  https://gitee.com/faib920/fireasy3/tests/Fireasy.Common.Tests/EmitTests.cs
  更多内容请移步官网 http://www.fireasy.cn 。

来源:https://www.cnblogs.com/fireasy/archive/2023/03/12/17201880.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具