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; }
    }
}

 

核心代碼

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>;
        }
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章