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
用于定义泛型类型参数。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://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 。
出处:http://fireasy.cnblogs.com
官网:http://www.fireasy.cn
版权声明:本文的版权归作者与博客园共有。转载时须注明本文的详细链接,否则作者将保留追究其法律责任。