首页 > 编程语言 >Fireasy3 揭秘 -- 使用 Emit 构建程序集

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

时间:2023-03-12 18:45:09浏览次数:52  
标签:-- Emit typeof var new null Fireasy3 public typeBuilder

目录

  • 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 命名空间下。如下:

    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://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 。

标签:--,Emit,typeof,var,new,null,Fireasy3,public,typeBuilder
From: https://www.cnblogs.com/fireasy/p/17201880.html

相关文章

  • mysql
    mysql数据库相关概念数据:数据库:存储数据的仓库,数据是有组织的进行存储数据库管理系统:操纵和管理数据库的大型软件SQL:结构化查询语言(structuredquerylanguage),是一套......
  • webpack、vite、vue-cli、create-vue 的区别
    首先说结论Rollup更适合打包库,webpack更适合打包项目应用,vite基于rollup实现了热更新也适合打包项目。功能工具工具脚手架vue-clicreate-vue构建项目 vit......
  • 函数
    函数的基本使用:无参函数:就是函数没有参数的函数。有参函数:函数内可以传值的函数。位置参数:函数的参数和调用的值一一对应的就是位置函数。关键字参数:以变量名=变量值的......
  • 09:矩阵乘法
    描述计算两个矩阵的乘法。n*m阶的矩阵A乘以m*k阶的矩阵B得到的矩阵C是n*k阶的,且C[i][j]=A[i][0]*B[0][j]+A[i][1]*B[1][j]+……+A[i][m-1]*B[m-1][j](C[i][j]表示......
  • LeeCode例题——二分查找
    1.二分查找:(面对一个升序排列的数组)classSoulution{public:intsearch(vector<int>&nums,inttarget){//函数名(数组,变量)intleft=0,right=nums.size()-......
  • Java中的线程状态
    Java中线程的状态New:新创建对象,还没执行start()方法RUNNABLE:就绪态和运行状态。BLOCKED:表示线程等到获取锁时候的状态。WAITING:处于这种状态的线程不会被分配CPU执行......
  • vim session 使用
    一、保存项目信息保存和加载会话信息session会话信息:当前编辑环境的空窗口、所有的缓冲区、当前目录、折叠(fold)相关的信息、帮助窗口、所有的选项和按键映射、所有的标......
  • 力扣180(MySQL)-连续出现的数字(中等)
    题目:编写一个SQL查询,查找所有至少连续出现三次的数字。返回的结果表中的数据可以按 任意顺序 排列。查询结果格式如下面的例子所示: 解题思路:原表数据: 方法......
  • ChatGPT 辅助 stable-diffusion 生成图片描述 tag 话术
    将如下话术发给ChatGPT:请用尽量多的英文单词描述一幅画,描述词尽量丰富,每个单词之间用逗号分隔:一个XXX 如果回复的tag数量不够,则追加四个字:不够丰富 之后Chat......
  • 表格1
    表格1这就是表格标题标题单元格标题单元格标题单元格标题单元格标题单元格单元格1单元格1单元格1单元格1单元格1单元格2单元格2单元格2单元格2单元格2......