1. 程式人生 > >Emit動態生成代理類用於監控物件的欄位修改

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
{
    /// &lt;summary&gt;
    /// 動態代理類生成器
    /// &lt;/summary&gt;
    public class DynamicProxyGenerator
    {
        private const string ModifiedPropertyNamesFieldName = "ChangePropertys";

        private const MethodAttributes GetSetMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.CheckAccessOnOverride | MethodAttributes.Virtual | MethodAttributes.HideBySig;
        /// &lt;summary&gt;
        /// 程式集名稱
        /// &lt;/summary&gt;
        private AssemblyName assemblyName { get; set; }
        /// &lt;summary&gt;
        /// 程式集構建器
        /// &lt;/summary&gt;
        private AssemblyBuilder assemblyBuilder { get; set; }

        /// &lt;summary&gt;
        /// 程式集模組
        /// &lt;/summary&gt;
        private ModuleBuilder moduleBuilder { get; set; }

        /// &lt;summary&gt;
        /// 儲存修改屬性的集合型別
        /// &lt;/summary&gt;
        private Type modifiedPropertyNamesType { get; set; }

        /// &lt;summary&gt;
        /// 構造一個動態代理生成器
        /// &lt;/summary&gt;
        /// &lt;param name="AssemblyName"&gt;&lt;/param&gt;
        /// &lt;param name="isSaveDynamicModule"&gt;&lt;/param&gt;
        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&lt;String, Object&gt;);
        }

        /// &lt;summary&gt;
        /// 註冊型別到代理生成器(只註冊Virtual屬性)
        /// &lt;/summary&gt;
        /// &lt;typeparam name="T"&gt;類&lt;/typeparam&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public void Register&lt;T&gt;()
        {
            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&lt;string&gt;();
            */
            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();
        }

        /// &lt;summary&gt;
        /// 儲存程式集到dll檔案
        /// &lt;/summary&gt;
        public void Save()
        {
            assemblyBuilder.Save($"{assemblyName.Name}.dll");
        }

        /// &lt;summary&gt;
        /// 建立例項
        /// &lt;/summary&gt;
        /// &lt;typeparam name="T"&gt;&lt;/typeparam&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public T CreateInstance&lt;T&gt;()
        {
            Type typeNeedProxy = typeof(T);
            return (T)assemblyBuilder.CreateInstance(typeNeedProxy.Name);
        }

        /// &lt;summary&gt;
        /// 獲取屬性的變更名稱,
        /// &lt;/summary&gt;
        /// &lt;param name="obj"&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public static Dictionary&lt;String, Object&gt; GetChangeProperties&lt;T&gt;(T obj)
        {
            FieldInfo fieldInfo = obj.GetType().GetField(ModifiedPropertyNamesFieldName);
            if (fieldInfo == null) return null;
            object value = fieldInfo.GetValue(obj);
            return value as Dictionary&lt;String, Object&gt;;
        }
    }
}

 

 

 

來源:https://blog.csdn.net/Vblegend_2013/article/details/85228041