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>;
}
}
}
```
来源:https://blog.csdn.net/Vblegend_2013/article/details/85228041
Emit动态生成代理类用于监控对象的字段修改的更多相关文章
- 秒懂C#通过Emit动态生成代码 C#使用Emit构造拦截器动态代理类
秒懂C#通过Emit动态生成代码 首先需要声明一个程序集名称, 1 // specify a new assembly name 2 var assemblyName = new Assembly ...
- JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析
通过前面几篇的分析,我们知道代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成 ...
- Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架
类加载器 Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader 类加载器也是Jav ...
- JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架
1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...
- Aop动态生成代理类时支持带参数构造函数
一.背景 在某些情况下,我们需要植入AOP代码的类并没有默认构造函数.那么此时动态生成的代理类也需要相同签名的构造函数,并且内部调用原始类的构造函数.自己折腾了1晚上没搞定,现在搞定了发出来供大家一起 ...
- 分析JVM动态生成的类
总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息? 三个方面: 1.生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知: 2.产生的类字节码必须有个一个关联的类加载器对象: 3.生 ...
- Emit动态生成代码
Emit动态生成代码 引用:秒懂C#通过Emit动态生成代码 首先需要声明一个程序集名称, // specify a new assembly name var assemblyName = new ...
- webservice 生成代理类
webservice的调用方式有两种: 1. 直接在vs ide中通过web引用的方式,将发布于某个位置的web服务引进到工程里面.这种方式基本上会用vs.net的人都会. 2. 通过vs 命令提 ...
- 按WSDL信息手动生成代理类
命令行: wsdl /language:c# /n:Entity /out:C:\Users\mengxianming\Desktop\Centrex_IMS_Client.cs C:\Users\m ...
随机推荐
- 软件工程第二次作业(Junit和GoogleTest)
使用Eclipse的Junit框架进行单元测试 一.前言 我的电脑因为以前学过安卓开发,已经安装好了eclipse,看了邹欣老师的博客,博客上讲的的非常详细,所以在进行的过程中遇到困难,基本都能解决. ...
- Win 10 蓝屏,出现DRIVER_POWER_STATE_FAILURE的解决方法
笔者个人笔记本电脑,用的是华硕的飞行堡垒FZ系列,上个月装了个Ubuntu的系统,之后换回Windows后,电脑疯狂蓝屏,错误代码只有这个DRIVER_POWER_STATE_FAILURE.一开始我 ...
- 赫然:Windows Live Writer 批量博客更新软件使用教程
http://www.wocaoseo.com/thread-144-1-1.html 推广人员需要使用多个博客,一个一个登陆更新是很麻烦的事情,网上的桌面批量更新博客软件也不少,今天在此推荐大家使用 ...
- [apue] 一图读懂 unix 文件句柄及文件共享过程
与文件相关的一些概念 在开始上图之前,先说明几个和 unix 文件密切相关的术语,方便后续讨论使用 文件句柄 / 文件描述符 (file descriptor 或 FD):描述一个打开文件相关属性的类 ...
- GET和POST的本质区别
前言:相信小伙伴们面试时候一定都遇到过这个问题,即使没有遇到过,至少也听说过,网上资料一大片,大概每个人都能说出来一些.但是总感觉面试装逼不成功,所以就翻阅了部分资料,进一步整理了下. 一般当我们提到 ...
- PlayerPrefs
PlayerPrefs http://blog.csdn.net/u011416077/article/details/47334963
- Tomcat之如何自己做一个tomcat
来源:<How Tomcat Works> Servlet容器的工作原理: 1.创建一个request对象并填充那些有可能被所引用的servlet使用的信息,比如参数.头部.cookies ...
- centOS7 设置mysql数据库外网可以访问
1.查看目前防火墙是否对外开发了3306端口 firewall-cmd --list-all 2.查看3306端口是否开放 firewall-cmd --query-port=3306/tcp no ...
- Nginx及其架构设计
1.1. 什么是 Nginx Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个 IMAP/P ...
- 如何使用JSTL获取并显示数据
首先在×××controller里查询数据,并绑定,代码如下: /** * 显示所有租借信息 默认进入这个方法 * * @param resp * @param req * @param manage ...