在運行期間,我們可以使用 `Emit` 來組織一段 IL 代碼,進而動態生成一個方法,甚至是一個程式集(包括類型、方法或屬性等等)。這個過程我們稱之為動態編織。這一項技術應用比較廣泛,比如數據映射(Dapper)、動態代理(AOP)等等,目的是提升大量反射而產生的性能問題。 ...
目錄
- 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 。
作者:fireasy出處:http://fireasy.cnblogs.com
官網:http://www.fireasy.cn
版權聲明:本文的版權歸作者與博客園共有。轉載時須註明本文的詳細鏈接,否則作者將保留追究其法律責任。