Emit動態生成代理類用於監控物件的欄位修改
利用Emit動態生成代理物件監控物件哪些欄位被修改,被修改為什麼值
被Register的物件要監控的值必須是Virtual虛型別
必須使用CreateInstance建立物件
必須使用DynamicProxyGenerator.GetChangeProperties 獲取改變的值
呼叫GetChangeProperties 返回的Dictionary.Clear() 重置當前已修改屬性
物件賦值時增加變動修改,如果value 和原始值相同則不記錄變動
支援註冊多個物件到一個代理程式集
核心部分摘自 https://blog.csdn.net/lishuangquan1987/article/details/84312514
測試程式碼
using System;
using System.Diagnostics;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
DynamicProxyGenerator dpg = new DynamicProxyGenerator("DynamicAssembly");
//註冊型別
dpg.Register<Person>();
dpg.Register<Person2>();
//儲存為dll
dpg.Save();
Person p = dpg.CreateInstance<Person>();
p.Name = "tom";
p.Age = 12345;
var changes = DynamicProxyGenerator.GetChangeProperties(p);
Console.WriteLine($"第一次檢測改變了{changes.Count}個屬性");
changes.Clear();
p.Name = "tony";
p.Age = 12345;
changes = DynamicProxyGenerator.GetChangeProperties(p);
Console.WriteLine($"第二次檢測改變了{changes.Count}個屬性");
//建立物件測試
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 100000; i++)
{
var p2 = dpg.CreateInstance<Person2>();
}
stopwatch.Stop();
Console.WriteLine($"建立物件100000個用時{stopwatch.ElapsedMilliseconds}ms");
Console.ReadKey();
}
}public class Person { public virtual String Name { get; set; } public virtual Int32 Age { get; set; } } public class Person2 { public virtual String Name { get; set; } public virtual Int32 Age { get; set; } }
}
<p> </p> <p>核心程式碼</p> <pre class="has"> <code class="language-cs">using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; namespace TestApp { /// <summary> /// 動態代理類生成器 /// </summary> public class DynamicProxyGenerator { private const string ModifiedPropertyNamesFieldName = "ChangePropertys"; private const MethodAttributes GetSetMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.CheckAccessOnOverride | MethodAttributes.Virtual | MethodAttributes.HideBySig; /// <summary> /// 程式集名稱 /// </summary> private AssemblyName assemblyName { get; set; } /// <summary> /// 程式集構建器 /// </summary> private AssemblyBuilder assemblyBuilder { get; set; } /// <summary> /// 程式集模組 /// </summary> private ModuleBuilder moduleBuilder { get; set; } /// <summary> /// 儲存修改屬性的集合型別 /// </summary> private Type modifiedPropertyNamesType { get; set; } /// <summary> /// 構造一個動態代理生成器 /// </summary> /// <param name="AssemblyName"></param> /// <param name="isSaveDynamicModule"></param> public DynamicProxyGenerator(String DynamicAssemblyName) { //建立程式集 assemblyName = new AssemblyName(DynamicAssemblyName); assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); //動態建立模組 moduleBuilder = assemblyBuilder.DefineDynamicModule(String.Format("{0}Module", DynamicAssemblyName), String.Format("{0}.dll", DynamicAssemblyName)); //修改的屬性集合型別 modifiedPropertyNamesType = typeof(Dictionary<String, Object>); } /// <summary> /// 註冊型別到代理生成器(只註冊Virtual屬性) /// </summary> /// <typeparam name="T">類</typeparam> /// <returns></returns> public void Register<T>() { Type typeNeedProxy = typeof(T); //建立動態類代理,這裡名字不變 繼承自T TypeBuilder typeBuilderProxy = moduleBuilder.DefineType(typeNeedProxy.Name, TypeAttributes.Public, typeNeedProxy); //定義一個Dictionary變數存放屬性變更名 FieldBuilder fbModifiedPropertyNames = typeBuilderProxy.DefineField(ModifiedPropertyNamesFieldName, modifiedPropertyNamesType, FieldAttributes.Public); /* * 建構函式 例項化 ModifiedPropertyNames,生成類似於下面的程式碼 ModifiedPropertyNames = new List<string>(); */ ConstructorBuilder constructorBuilder = typeBuilderProxy.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null); ILGenerator ilgCtor = constructorBuilder.GetILGenerator(); ilgCtor.Emit(OpCodes.Ldarg_0);//載入當前類 ilgCtor.Emit(OpCodes.Newobj, modifiedPropertyNamesType.GetConstructor(new Type[0]));//例項化物件入棧 ilgCtor.Emit(OpCodes.Stfld, fbModifiedPropertyNames);//設定fbModifiedPropertyNames值,為剛入棧的例項化物件 ilgCtor.Emit(OpCodes.Ret);//返回 //獲取被代理物件的所有屬性,迴圈屬性進行重寫 PropertyInfo[] properties = typeNeedProxy.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { string propertyName = propertyInfo.Name; Type typePepropertyInfo = propertyInfo.PropertyType; //動態建立欄位和屬性 FieldBuilder fieldBuilder = typeBuilderProxy.DefineField("_" + propertyName.ToLower(), typePepropertyInfo, FieldAttributes.Private); PropertyBuilder propertyBuilder = typeBuilderProxy.DefineProperty(propertyName, PropertyAttributes.SpecialName, typePepropertyInfo, null); //重寫屬性的Get Set方法 var methodGet = typeBuilderProxy.DefineMethod("get_" + propertyName, GetSetMethodAttributes, typePepropertyInfo, Type.EmptyTypes); var methodSet = typeBuilderProxy.DefineMethod("set_" + propertyName, GetSetMethodAttributes, null, new Type[] { typePepropertyInfo }); //il of get method var ilGetMethod = methodGet.GetILGenerator(); ilGetMethod.Emit(OpCodes.Ldarg_0); ilGetMethod.Emit(OpCodes.Ldfld, fieldBuilder); ilGetMethod.Emit(OpCodes.Ret); //il of set method ILGenerator ilSetMethod = methodSet.GetILGenerator(); //宣告布林型別 ilSetMethod.DeclareLocal(typeof(Boolean)); //宣告一個標籤,標記到ret Label label = ilSetMethod.DefineLabel(); //判斷值是否改變 ilSetMethod.Emit(OpCodes.Nop); ilSetMethod.Emit(OpCodes.Ldarg_0); ilSetMethod.Emit(OpCodes.Ldfld, fieldBuilder); ilSetMethod.Emit(OpCodes.Ldarg_1); ilSetMethod.Emit(OpCodes.Ceq); ilSetMethod.Emit(OpCodes.Ldc_I4_0); ilSetMethod.Emit(OpCodes.Ceq); ilSetMethod.Emit(OpCodes.Stloc_0); ilSetMethod.Emit(OpCodes.Ldloc_0); //如果未改變,調到結束return ilSetMethod.Emit(OpCodes.Brfalse_S, label); //賦值 ilSetMethod.Emit(OpCodes.Nop); ilSetMethod.Emit(OpCodes.Ldarg_0); ilSetMethod.Emit(OpCodes.Ldarg_1); ilSetMethod.Emit(OpCodes.Stfld, fieldBuilder); //儲存到 Dictionary ilSetMethod.Emit(OpCodes.Ldarg_0); ilSetMethod.Emit(OpCodes.Ldfld, fbModifiedPropertyNames); ilSetMethod.Emit(OpCodes.Ldstr, propertyInfo.Name); ilSetMethod.Emit(OpCodes.Ldarg_1); ilSetMethod.Emit(OpCodes.Box, propertyInfo.PropertyType); ilSetMethod.Emit(OpCodes.Callvirt, modifiedPropertyNamesType.GetMethod("set_Item", new Type[] { typeof(string), typeof(Object) })); ilSetMethod.Emit(OpCodes.Nop); ilSetMethod.Emit(OpCodes.Nop); ilSetMethod.MarkLabel(label); ilSetMethod.Emit(OpCodes.Ret); //設定屬性的Get Set方法 propertyBuilder.SetGetMethod(methodGet); propertyBuilder.SetSetMethod(methodSet); } //引用下, 要不無法生成 Type proxyClassType = typeBuilderProxy.CreateType(); } /// <summary> /// 儲存程式集到dll檔案 /// </summary> public void Save() { assemblyBuilder.Save($"{assemblyName.Name}.dll"); } /// <summary> /// 建立例項 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public T CreateInstance<T>() { Type typeNeedProxy = typeof(T); return (T)assemblyBuilder.CreateInstance(typeNeedProxy.Name); } /// <summary> /// 獲取屬性的變更名稱, /// </summary> /// <param name="obj"></param> /// <returns></returns> public static Dictionary<String, Object> GetChangeProperties<T>(T obj) { FieldInfo fieldInfo = obj.GetType().GetField(ModifiedPropertyNamesFieldName); if (fieldInfo == null) return null; object value = fieldInfo.GetValue(obj); return value as Dictionary<String, Object>; } } }
來源:https://blog.csdn.net/Vblegend_2013/article/details/85228041