利用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; }
}
}
核心代碼
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>;
}
}
}