|
目录
- 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 用于定义泛型类型参数。GtpType 的 Initialize 方法是将基类约束、接口约束等设定到 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 。
来源:https://www.cnblogs.com/fireasy/archive/2023/03/12/17201880.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|