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

目录

  • 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 揭秘 -- 用对象池技术管理上下文
  • 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 命名空间下。如下:

    public class DynamicAssemblyBuilder : DynamicBuilder
    {
        private AssemblyBuilder _assemblyBuilder;
        private ModuleBuilder _moduleBuilder;

        private AssemblyBuilder InitAssemblyBuilder()
        {
            if (_assemblyBuilder == null)
            {
                var an = new AssemblyName(AssemblyName);
                if (string.IsNullOrEmpty(OutputAssembly))
                {
#if NETFRAMEWORK
                    _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
#else
                    _assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndCollect);
#endif
                }
                else
                {
#if NETFRAMEWORK
                    var dir = Path.GetDirectoryName(OutputAssembly);
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }

                    _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave, dir);
#else
                    _assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
#endif
                }
            }

            return _assemblyBuilder;
        }

        /// <summary>
        /// 获取 <see cref="ModuleBuilder"/> 对象。
        /// </summary>
        /// <returns></returns>
        private ModuleBuilder InitModuleBuilder()
        {
            if (_moduleBuilder == null)
            {
                if (string.IsNullOrEmpty(OutputAssembly))
                {
                    _moduleBuilder = AssemblyBuilder.DefineDynamicModule("Main");
                }
                else
                {
                    var fileName = OutputAssembly.Substring(OutputAssembly.LastIndexOf("\\") + 1);
#if NETFRAMEWORK
                    _moduleBuilder = AssemblyBuilder.DefineDynamicModule(fileName, fileName);
#else
                    _moduleBuilder = AssemblyBuilder.DefineDynamicModule(fileName);
#endif
                }
            }

            return _moduleBuilder;
        }
    }

  在 .net framework 时代,我们可以将动态构造的程序集存储到磁盘中,很遗憾,从 .net standard 之后就无法实现这一功能了,这给我们带来了诸多不便,因为动态生成的程序集必须存储在内存当中了。

  然后,DynamicAssemblyBuilder 提供了定义接口、类型和枚举的方法,将工作交给这些不同的构造器。如下:

        /// <summary>
        /// 使用当前的构造器定义一个动态类型。
        /// </summary>
        /// <param name="typeName">类型的名称。</param>
        /// <param name="accessibility">指定类的可见性。</param>
        /// <param name="modifier">指定类的调用属性。</param>
        /// <param name="baseType">类型的父类。</param>
        /// <returns></returns>
        public DynamicTypeBuilder DefineType(string typeName, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Type baseType = null)
        {
            var typeBuilder = new DynamicTypeBuilder(Context, typeName, accessibility, modifier, baseType);
            _typeBuilders.Add(typeBuilder);
            return typeBuilder;
        }

        /// <summary>
        /// 使用当前的构造器定义一个动态接口。
        /// </summary>
        /// <param name="typeName">类型的名称。</param>
        /// <param name="accessibility">指定类的可见性。</param>
        /// <returns></returns>
        public DynamicInterfaceBuilder DefineInterface(string typeName, Accessibility accessibility = Accessibility.Public)
        {
            var typeBuilder = new DynamicInterfaceBuilder(Context, typeName, accessibility);
            _typeBuilders.Add(typeBuilder);
            return typeBuilder;
        }

        /// <summary>
        /// 使用当前构造器定义一个枚举。
        /// </summary>
        /// <param name="enumName">枚举的名称。</param>
        /// <param name="underlyingType">枚举的类型。</param>
        /// <param name="accessibility">指定枚举的可见性。</param>
        /// <returns></returns>
        public DynamicEnumBuilder DefineEnum(string enumName, Type? underlyingType = null, Accessibility accessibility = Accessibility.Public)
        {
            var enumBuilder = new DynamicEnumBuilder(Context, enumName, underlyingType ?? typeof(int), accessibility);
            _typeBuilders.Add(enumBuilder);
            return enumBuilder;
        }

  所以 DynamicAssemblyBuilder 最多只能算是一个容器,更多的工作分解到各种构造器中。上面的方法中,定义接口、类型、枚举时,都将相应的构造器放到 _typeBuilders 集合里,在最后调用 Create 方法逐一创建。如下:

        /// <summary>
        /// 创建程序集。
        /// </summary>
        /// <returns></returns>
        public Assembly Create()
        {
            if (!_isCreated)
            {
                foreach (var typeBuilder in _typeBuilders)
                {
                    typeBuilder.CreateType();
                }

                _isCreated = true;
            }

            return AssemblyBuilder;
        }

DynamicTypeBuilder

  动态类型构造器是对 TypeBuilder 的包装。这里需要强调的是,在 DefineType 时,类的访问性由 Accessibility 枚举来指定,如 public、private、protected、internal 等等,修饰性则由 Modifier 枚举来指定,如 abstract、sealed。以下的方法主要用来设定类型的的 TypeAttributes,如下:

        private TypeAttributes GetTypeAttributes(Accessibility accessibility, Modifier modifier)
        {
            var attrs = GetTypeAttributes();
            switch (modifier)
            {
                case Modifier.Abstract:
                    attrs |= TypeAttributes.Abstract;
                    break;
                case Modifier.Sealed:
                    attrs |= TypeAttributes.Sealed;
                    break;
            }

            switch (accessibility)
            {
                case Accessibility.Internal:
                    if (_isNesetType)
                    {
                        attrs |= TypeAttributes.NestedAssembly;
                    }

                    break;
                case Accessibility.Private:
                    if (_isNesetType)
                    {
                        attrs |= TypeAttributes.NestedPrivate;
                    }

                    break;
                case Accessibility.Public:
                    attrs |= _isNesetType ? TypeAttributes.NestedPublic : TypeAttributes.Public;
                    break;
            }

            return attrs;
        }

  这里重点要介绍一下泛型类型参数的处理。为了兼容定义方法时的泛型类型参数,这里定义了一个 GtpType 类型(Generic Type Parameter),它继承自 Type 类型,主要用到 Name 属性,其他属性均忽略。另外,通过以下的方法设定约束:

    /// <summary>
    /// 用于标识一个泛型类型参数的类型。无法继承此类。
    /// </summary>
    public sealed class GtpType : Type
    {
        private Type? _baseType;
        private Type[]? _constraintTypes;
        private GenericParameterAttributes? _parameterAttributes;

        /// <summary>
        /// 设置基类约束。
        /// </summary>
        /// <param name="baseType">基类类型。</param>
        /// <returns></returns>
        public GtpType SetBaseTypeConstraint(Type baseType)
        {
            _baseType = baseType;
            return this;
        }

        /// <summary>
        /// 设置接口约束。
        /// </summary>
        /// <param name="constraintTypes">约束类型。</param>
        /// <returns></returns>
        public GtpType SetInterfaceConstraints(params Type[] constraintTypes)
        {
            _constraintTypes = constraintTypes;
            return this;
        }

        /// <summary>
        /// 设置参数特性。
        /// </summary>
        /// <param name="attributes"></param>
        /// <returns></returns>
        public GtpType SetGenericParameterAttributes(GenericParameterAttributes attributes)
        {
            _parameterAttributes = attributes;
            return this;
        }
    }

TypeBuilder 提供了一个方法 DefineGenericParameters 用于定义泛型类型参数。GtpTypeInitialize 方法是将基类约束、接口约束等设定到 GenericTypeParameterBuilder 对象里。如下:

        /// <summary>
        /// 定义泛型参数。
        /// </summary>
        /// <param name="parameterTypes"></param>
        /// <returns></returns>
        public void DefineGenericParameters(params GtpType[] parameterTypes)
        {
            if (_genericParameterTypes != null)
            {
                throw new InvalidOperationException("已经定义过泛型参数。");
            }

            _genericParameterTypes = parameterTypes.ToDictionary(s => s.Name);
            var builders = _typeBuilder.DefineGenericParameters(parameterTypes.Select(s => s.Name).ToArray());

            for (var i = 0; i < parameterTypes.Length; i++)
            {
                parameterTypes[i].Initialize(builders[i]);
            }
        }

  然后,DynamicTypeBuilder 提供了定义方法、构造函数、属性和嵌套类型的方法,将工作交给这些不同的构造器。如下:

        /// <summary>
        /// 定义一个属性。
        /// </summary>
        /// <param name="propertyName">属性的名称。</param>
        /// <param name="propertyType">属性的类型。</param>
        /// <param name="accessibility">指定属性的可见性。</param>
        /// <param name="modifier">指定属性的调用属性。</param>
        /// <returns>新的 <see cref="DynamicPropertyBuilder"/>。</returns>
        public virtual DynamicPropertyBuilder DefineProperty(string propertyName, Type propertyType, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard)
        {
            return new DynamicPropertyBuilder(Context, propertyName, propertyType, accessibility, modifier);
        }

        /// <summary>
        /// 定义一个方法。
        /// </summary>
        /// <param name="methodName">方法的名称。</param>
        /// <param name="returnType">返回值的类型,如果为 void 则该参数为 null。</param>
        /// <param name="parameterTypes">一个数组,表示方法的传入参数类型。</param>
        /// <param name="accessibility">指定方法的可见性。</param>
        /// <param name="modifier">指定方法的调用属性。</param>
        /// <param name="ilCoding">方法体的 IL 过程。</param>
        /// <returns>新的 <see cref="DynamicMethodBuilder"/>。</returns>
        public virtual DynamicMethodBuilder DefineMethod(string methodName, Type? returnType = null, Type[]? parameterTypes = null, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
        {
            return new DynamicMethodBuilder(Context, methodName, returnType, parameterTypes, accessibility, modifier, ilCoding);
        }

        /// <summary>
        /// 定义一个构造函数。
        /// </summary>
        /// <param name="parameterTypes"></param>
        /// <param name="accessibility"></param>
        /// <param name="modifier"></param>
        /// <param name="ilCoding"></param>
        /// <returns></returns>
        public virtual DynamicConstructorBuilder DefineConstructor(Type[] parameterTypes, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
        {
            return new DynamicConstructorBuilder(Context, parameterTypes, accessibility, modifier, ilCoding);
        }

        /// <summary>
        /// 定义一个字段。
        /// </summary>
        /// <param name="fieldName">字段的名称。</param>
        /// <param name="fieldType">字段的类型。</param>
        /// <param name="defaultValue">默认值。</param>
        /// <param name="accessibility"></param>
        /// <param name="modifier"></param>
        /// <returns></returns>
        public virtual DynamicFieldBuilder DefineField(string fieldName, Type fieldType, object? defaultValue = null, Accessibility accessibility = Accessibility.Private, Modifier modifier = Modifier.Standard)
        {
            return new DynamicFieldBuilder(Context, fieldName, fieldType, defaultValue, accessibility, modifier);
        }

        /// <summary>
        /// 定义一个嵌套的类型。
        /// </summary>
        /// <param name="typeName"></param>
        /// <param name="accessibility"></param>
        /// <param name="baseType"></param>
        /// <returns></returns>
        public virtual DynamicTypeBuilder DefineNestedType(string typeName, Accessibility accessibility = Accessibility.Private, Type? baseType = null)
        {
            var nestedType = new DynamicTypeBuilder(Context, typeName, accessibility, baseType);
            _nestedTypeBuilders.Add(nestedType);
            return nestedType;
        }

        /// <summary>
        /// 使用当前的构造器定义一个动态接口。
        /// </summary>
        /// <param name="typeName">类型的名称。</param>
        /// <param name="accessibility">指定类的可见性。</param>
        /// <returns></returns>
        public DynamicInterfaceBuilder DefineNestedInterface(string typeName, Accessibility accessibility = Accessibility.Public)
        {
            var typeBuilder = new DynamicInterfaceBuilder(Context, typeName, accessibility);
            _nestedTypeBuilders.Add(typeBuilder);
            return typeBuilder;
        }

        /// <summary>
        /// 使用当前构造器定义一个枚举。
        /// </summary>
        /// <param name="enumName">枚举的名称。</param>
        /// <param name="underlyingType">枚举的类型。</param>
        /// <param name="accessibility">指定枚举的可见性。</param>
        /// <returns></returns>
        public DynamicEnumBuilder DefineNestedEnum(string enumName, Type? underlyingType = null, Accessibility accessibility = Accessibility.Public)
        {
            var enumBuilder = new DynamicEnumBuilder(Context, enumName, underlyingType ?? typeof(int), accessibility);
            _nestedTypeBuilders.Add(enumBuilder);
            return enumBuilder;
        }

DynamicMethodBuilder

  动态方法构造器在定义时,需要指定参数类型,返回类型等等,对于泛型方法,也需要使用 GtpType 类型来定义。以下的方法是用于处理泛型方法的:

        private void ProcessGenericMethod()
        {
            Dictionary<string, GenericTypeParameterBuilder>? builders = null;

            //方法参数里有泛型类型参数
            if (ParameterTypes?.Any(s => s is GtpType) == true)
            {
                //筛选没有在类型构造器里定义过的泛型类型参数
                var names = ParameterTypes.Where(s => s is GtpType).Where(s => !Context.TypeBuilder.TryGetGenericParameterType(s.Name, out _)).Cast<GtpType>().Select(s => s.Name).ToArray();

                //如果有新的泛型类型参数,则在方法构造器里定义
                if (names.Length > 0)
                {
                     builders = _methodBuilder.DefineGenericParameters(names).ToDictionary(s => s.Name);
                }

                for (var i = 0; i < ParameterTypes.Length; i++)
                {
                    if (ParameterTypes[i] is GtpType gt)
                    {
                        if (builders?.TryGetValue(gt.Name, out var parb) == true)
                        {
                            ParameterTypes[i] = gt.Initialize(parb);
                        }
                        else if (Context.TypeBuilder.TryGetGenericParameterType(gt.Name, out var gt1))
                        {
                            ParameterTypes[i] = gt1.GenericTypeParameterBuilder;
                        }
                    }
                }

                MethodBuilder.SetParameters(ParameterTypes);
            }

            //如果返回类型是泛型类型
            if (ReturnType is GtpType rgt)
            {
                //先在方法构造器里查找
                if (builders?.TryGetValue(rgt.Name, out var retb) == true)
                {
                    ReturnType = rgt.Initialize(retb);
                }
                //在类型构造器里查找
                else if (Context.TypeBuilder.TryGetGenericParameterType(rgt.Name, out var gt1))
                {
                    ReturnType = gt1.GenericTypeParameterBuilder;
                }
            }
        }

  这样,就完美处理了泛型方法,文章最后会例举测试用例进一步加深印象。
  如果动态类型指定了所继承的基类,还需要处理重载方法,以下的方法用于在父类中查找与名称和参数相匹配的方法:

        private MethodInfo? FindMethod(string methodName, IEnumerable<Type> parameterTypes)
        {
            MethodInfo? method = null;
            if (Context.TypeBuilder.BaseType != null)
            {
                method = Helper.MatchMethod(Context.TypeBuilder.BaseType, methodName, parameterTypes);

                if (method != null && !method.IsVirtual)
                {
                    throw new DynamicBuildException("所定义的方法在父类中未标记 virtual、abstract 或 override。");
                }
            }

            //在实现的接口中查找方法
            var interfaceTypes = Context.TypeBuilder.InterfaceTypes
                .Union(Context.TypeBuilder.InterfaceTypes.SelectMany(s => s.GetInterfaces()))
                .Distinct().ToList();

            //在实现接口中去查找方法
            if (method == null && interfaceTypes.Count != 0)
            {
                foreach (var type in interfaceTypes)
                {
                    method = type.GetMethod(methodName, parameterTypes == null ? Type.EmptyTypes : parameterTypes.ToArray());
                    if (method != null)
                    {
                        break;
                    }
                }
            }

            return method;
        }

  在处理 MethodAttributes 时,如果匹配到父类的方法,则也会有不同的处理,这些属性的含义,需要自己去慢慢理解。如下:

        private MethodAttributes GetMethodAttributes(string methodName, IEnumerable<Type> parameterTypes, Accessibility accessibility, Modifier modifier)
        {
            var method = FindMethod(methodName, parameterTypes);
            var isOverride = method != null && method.IsVirtual;
            var isInterface = isOverride && method!.DeclaringType!.IsInterface;
            var isBaseType = isOverride && method!.DeclaringType == Context.TypeBuilder.BaseType;
            if (method != null)
            {
                Context.BaseMethod = method;
            }

            var attrs = GetMethodAttributes(accessibility, modifier);
            if (isOverride)
            {
                attrs |= MethodAttributes.Virtual;

                //去掉 NewSlot
                if (isBaseType && _attributes.HasFlag(MethodAttributes.NewSlot))
                {
                    attrs &= ~MethodAttributes.NewSlot;
                }
                else if (isInterface)
                {
                    //如果没有传入 modifier,则加 Final 去除上面定义的 Virtual
                    if (modifier == Modifier.Standard)
                    {
                        attrs |= MethodAttributes.Final;
                    }

                    attrs |= MethodAttributes.NewSlot;
                }
            }
            else if (method != null)
            {
            }

            return attrs;
        }

  在 DefineMethod 方法中,最后一个参数 ilCoding 允许你用 IL 指令编写一段代码,以作为方法体。Emitter 属性是一个 EmitHelper 对象,它是对 ILGenerator 对象的包装,可以更方便地使用链式语法编写 IL 指令。从构造函数里调用 InitBuilder 方法可以看出它的工作原理:

        private void InitBuilder()
        {
            //此处略去
            Context.Emitter = new EmitHelper(_methodBuilder.GetILGenerator(), _methodBuilder);
            //此处略去
            if (_buildAction != null)
            {
                _buildAction(Context);
            }
            else
            {
                Context.Emitter.ret();
            }
        }

  通过 DefineMethod 方法编写 IL 指令后,还可以使用 OverwriteCode 方法进行覆盖,或使用 AppendCode 方法进行追加,如下:

        /// <summary>
        /// 追加新的 MSIL 代码到构造器中。
        /// </summary>
        /// <param name="ilCoding"></param>
        /// <returns></returns>
        public DynamicMethodBuilder AppendCode(Action<EmitHelper> ilCoding)
        {
            ilCoding?.Invoke(Context.Emitter);

            return this;
        }

        /// <summary>
        /// 使用新的 MSIL 代码覆盖构造器中的现有代码。
        /// </summary>
        /// <param name="ilCoding"></param>
        /// <returns></returns>
        public DynamicMethodBuilder OverwriteCode(Action<EmitHelper> ilCoding)
        {
            var field = typeof(MethodBuilder).GetField("m_ilGenerator", BindingFlags.NonPublic | BindingFlags.Instance);
            if (field != null)
            {
                var cons = typeof(ILGenerator).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
                field.SetValue(_methodBuilder, cons.Invoke(new[] { _methodBuilder }));
                Context.Emitter = new EmitHelper(_methodBuilder.GetILGenerator(), _methodBuilder);
            }

            return AppendCode(ilCoding);
        }

DynamicPropertyBuilder

  动态属性构造器就要容易一些,只需要用它来定义 get 和 set 方法,它一般是自动属性的,如果要使用到字段域,则传入字段域构造器即可。如果你的 get 或 set 方法很复杂,那就使用 ilCoding 进行指令编码。如下:

        /// <summary>
        /// 获取当前的 <see cref="DynamicFieldBuilder"/>。
        /// </summary>
        /// <returns></returns>
        public DynamicFieldBuilder FieldBuilder
        {
            get
            {
                return _fieldBuilder ?? (_fieldBuilder = Context.TypeBuilder.DefineField(string.Format("m_<{0}>", Name), PropertyType));
            }
        }

        /// <summary>
        /// 定义属性的 Get 访问方法。
        /// </summary>
        /// <param name="accessibility">指定方法的可见性。</param>
        /// <param name="modifier">指定方法的调用属性。</param>
        /// <param name="ilCoding">方法体的 IL 过程。</param>
        /// <returns>新的 <see cref="DynamicMethodBuilder"/>。</returns>
        public DynamicMethodBuilder DefineGetMethod(Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
        {
            var isInterface = Context.TypeBuilder is DynamicInterfaceBuilder;
            var method = new DynamicMethodBuilder(Context, string.Concat("get_", GetMethodName()), PropertyType, Type.EmptyTypes, accessibility, modifier, ctx =>
                {
                    if (isInterface)
                    {
                        return;
                    }

                    if (ilCoding != null)
                    {
                        ilCoding(ctx);
                    }
                    else
                    {
                        ctx.Emitter.ldarg_0.ldfld(FieldBuilder.FieldBuilder).ret();
                    }
                });
            PropertyBuilder.SetGetMethod(method.MethodBuilder);
            return method;
        }

        /// <summary>
        /// 定义属性的 Get 访问方法。
        /// </summary>
        /// <param name="accessibility">指定方法的可见性。</param>
        /// <param name="modifier">指定方法的调用属性。</param>
        /// <param name="fieldBuilder">指定一个属性相关的 <see cref="DynamicFieldBuilder"/>。</param>
        /// <returns>新的 <see cref="DynamicMethodBuilder"/>。</returns>
        public DynamicMethodBuilder DefineGetMethodByField(Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, DynamicFieldBuilder? fieldBuilder = null)
        {
            var isInterface = Context.TypeBuilder is DynamicInterfaceBuilder;
            var method = new DynamicMethodBuilder(Context, string.Concat("get_", GetMethodName()), PropertyType, Type.EmptyTypes, accessibility, modifier, ctx =>
                {
                    if (isInterface)
                    {
                        return;
                    }

                    fieldBuilder ??= FieldBuilder;

                    ctx.Emitter.ldarg_0.ldfld(fieldBuilder.FieldBuilder).ret();
                });

            PropertyBuilder.SetGetMethod(method.MethodBuilder);
            return method;
        }

DynamicConstructorBuilder

动态构造函数构造器,如果类型继承了基类,则默认的方法体需要调用父类构造函数。如下:

        internal DynamicConstructorBuilder(BuildContext context, Type[] parameterTypes, Accessibility accessibility = Accessibility.Public, Modifier modifier = Modifier.Standard, Action<BuildContext> ilCoding = null)
            : base(accessibility, modifier)
        {
            Context = new BuildContext(context) { ConstructorBuilder = this };
            ParameterTypes = parameterTypes;

            if (ilCoding == null)
            {
                if (context.TypeBuilder.BaseType != typeof(object))
                {
                    var constructor = Helper.MatchConstructor(Context.TypeBuilder.BaseType, parameterTypes);

                    if (constructor != null)
                    {
                        ilCoding = c => c.Emitter.ldarg_0
                            .If(parameterTypes != null, b => b.For(0, parameterTypes!.Length, (e, i) => e.ldarg(i + 1)))
                            .call(constructor).ret();
                    }
                }
            }

            ilCoding ??= c => c.Emitter.ret();

            _buildAction = ilCoding;
            _attributes = GetMethodAttributes(accessibility, modifier);
            InitBuilder();
        }

  最后,说一下关于自定义特性。在构造器基类 DynamicBuilder 里,有一个 SetCustomAttribute 方法,它可以使用 lambda 表达式来指定自定义特性。

测试用例

  创建一个类型,继承基类,实现接口:

        [TestMethod]
        public void TestTypeBuilder()
        {
            /*
            public class MyClass : MyBaseClass, IMyInterface
            {
                public string Title { get; set; }
                public void HelloWorld()
                {
                }
                public void WriteName(string a1, string a2)
                {
                }
            }
            */

            var assemblyBuilder = new DynamicAssemblyBuilder("MyAssembly");
            var typeBuilder = assemblyBuilder.DefineType("MyClass");

            typeBuilder.BaseType = typeof(MyBaseClass);

            typeBuilder.ImplementInterface(typeof(IMyInterface));

            var methodBuilder = typeBuilder.DefineMethod("HelloWorld");
            var propertyBuilder = typeBuilder.DefineProperty("Title", typeof(string)).DefineGetSetMethods();

            methodBuilder = typeBuilder.DefineMethod("WriteName", typeof(string), new[] { typeof(string) });

            var type = typeBuilder.CreateType();

            Assert.IsTrue(typeof(IMyInterface).IsAssignableFrom(type));
        }

  创建一个泛型类型,以及泛型方法:

        [TestMethod]
        public void TestDefineGenericType()
        {
            /*
            public class MyClass<T, TS> where T : MyBaseClass
            {
                public MyClass(TS ts)
                {
                }
                public T Hello<TV>(T t, TV tv)
                {
                    Console.WriteLine(tv);
                    return t;
                }
            }
            */

            var gt = new GtpType("T").SetBaseTypeConstraint(typeof(MyBaseClass));

            var assemblyBuilder = new DynamicAssemblyBuilder("MyAssembly");
            var typeBuilder = assemblyBuilder.DefineType("MyClass");
            //定义泛型类型参数
            typeBuilder.DefineGenericParameters(gt, new GtpType("TS"));

            //定义构造函数
            typeBuilder.DefineConstructor(new Type[] { new GtpType("TS") });

            //定义一个泛型方法,TV不在类中定义,所以属于方法的泛型类型参数
            var methodBuilder = typeBuilder.DefineMethod("Hello", gt, new Type[] { gt, new GtpType("TV") }, ilCoding: c =>
            {
                c.Emitter
                .ldarg_2.call(typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }))
                .ldarg_1.ret();
            });

            var type = typeBuilder.CreateType().MakeGenericType(typeof(MyBaseClass), typeof(int));
            var obj = Activator.CreateInstance(type, 100);

            var method = type.GetMethod("Hello").MakeGenericMethod(typeof(string));
            var value = method.Invoke(obj, new object[] { new MyBaseClass(), "world" });

            Assert.IsInstanceOfType(value, typeof(MyBaseClass));
        }

  定义泛型方法:

        /// <summary>
        /// 使用泛型参数测试DefineMethod方法。
        /// </summary>
        [TestMethod()]
        public void TestDefineGenericMethod()
        {
            var typeBuilder = CreateBuilder();

            /*
            public class testClass
            {
                public void Hello<T1, T2>(string name, T1 any1, T2 any2)
                {
                    Console.Write(name + any1 + any2);
                }
            }
            */
            var methodBuilder = typeBuilder.DefineMethod("Hello", parameterTypes: new Type[] { typeof(string), new GtpType("T1"), new GtpType("T2") });
            methodBuilder.DefineParameter("name");
            methodBuilder.DefineParameter("any1");
            methodBuilder.DefineParameter("any2");

            var paraCount = methodBuilder.ParameterTypes.Length;

            methodBuilder.OverwriteCode(e =>
            {
                e.ldc_i4(paraCount)
                .newarr(typeof(object))
                .dup.ldc_i4_0.ldarg_1.stelem_ref
                .For(1, paraCount, (e1, i) =>
                {
                    e1.dup.ldc_i4(i).ldarg(i + 1).box(methodBuilder.ParameterTypes[i]).stelem_ref.end();
                })
                .call(typeof(string).GetMethod("Concat", new[] { typeof(object[]) }))
                .call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }))
                .ret();
            });

            var type = typeBuilder.CreateType();

            var method = type.GetMethod("Hello");
            Assert.IsNotNull(method);
            Assert.IsTrue(method.IsGenericMethod);

            var obj = Activator.CreateInstance(type);

            method = method.MakeGenericMethod(typeof(int), typeof(decimal));

            method.Invoke(obj, new object[] { "fireasy", 22, 45m });
        }

  显式实现方法:

        /// <summary>
        /// 使用接口成员显式实现测试ImplementInterface方法。
        /// </summary>
        [TestMethod()]
        public void ImplementInterfaceWithExplicitMember()
        {
            /*
            public class testClass : IDynamicMethodInterface
            {
                void IDynamicMethodInterface.Test(int s)
                {
                    Console.WriteLine(s);
                }
            }
            */
            var typeBuilder = CreateBuilder();

            typeBuilder.ImplementInterface(typeof(IDynamicMethodInterface));
            var methodBuilder = typeBuilder.DefineMethod("Test",
                parameterTypes: new[] { typeof(int) },
                modifier: Modifier.ExplicitImpl,
                ilCoding: (e) => e.Emitter.ldstr("fireasy").call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })).ret());

            methodBuilder.DefineParameter("s");

            var type = typeBuilder.CreateType();

            var obj = Activator.CreateInstance(type) as IDynamicMethodInterface;
            obj.Test(111);

            Assert.IsTrue(typeof(IDynamicMethodInterface).IsAssignableFrom(type));
        }

  继承泛型基类,并且定义构造函数:

        /// <summary>
        /// 测试DefineConstructor方法。
        /// </summary>
        [TestMethod()]
        public void TestDefineConstructorForGeneric()
        {
            var typeBuilder = CreateBuilder();

            /*
            public class testClass<T> : GenericClass<T>
            {
                public testClass(T value)
                    : base (value)
                {
                }
            }
            */

            var gtp = new GtpType("T");
            typeBuilder.BaseType = typeof(GenericClass<>);
            typeBuilder.DefineGenericParameters(gtp);

            var constructorBuilder = typeBuilder.DefineConstructor(new Type[] { gtp });
            constructorBuilder.DefineParameter("value");

            var type = typeBuilder.CreateType();

            type = type.MakeGenericType(typeof(string));

            var obj = Activator.CreateInstance(type, new[] { "fireasy" });
            Assert.IsNotNull(obj);

        }

  最后,奉上 Fireasy 3 的开源地址:https://github.com/faib920/fireasy3 ,欢迎大家前来捧场。

  本文相关代码请参考:
  https://github.com/faib920/fireasy3/src/libraries/Fireasy.Common/Emit
  https://github.com/faib920/fireasy3/tests/Fireasy.Common.Tests/EmitTests.cs

  更多内容请移步官网 http://www.fireasy.cn 。

posted @ 2023-03-12 18:32  fireasy  阅读(156)  评论(2编辑  收藏  举报