前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c#通过Emit方式实现动态代理

c#通过Emit方式实现动态代理

作者头像
code2roc
发布2023-07-19 14:36:32
3630
发布2023-07-19 14:36:32
举报

之前自己在写一个IOC小轮子的时候,临时想加一个动态代理拦截功能,考虑到实用性方面,使用了Emit动态生成的方式代替RealProxy加反射的实现,网上查找过不少版本,但是都存在一些缺陷,所以决定自己实现一个。

首先了解一下动态代理的原理,在编码过程中,如果对原有代码不想做改动,且对操作前操作后加入一些迭代代码,我们会使用静态代理,也就是新建一个类,持有原实现类的引用,写一个同名方法并在其中调用,大概的编码形式如下:

代码语言:javascript
复制
public class MovieProxy
    {
        
        private Movie _beproxy = new Movie();
        public override void Test()
        {
            Console.WriteLine("Before");
            _beproxy.Test();
            Console.WriteLine("After");
          
        }
       
    }

更灵活一步的实现方式可以改造为继承基类,或者实现接口

代码语言:javascript
复制
 public class MovieProxy : Movie
    {
       
        public override void Test()
        {
            Console.WriteLine("Before");
            base.Test();
            Console.WriteLine("After");
        }
       
    }

这样实现也随之带来一个问题,如果我有很多类或者一个类中需要代理的方法很多,编码就会做很多重复的操作,所以我们需要通过动态代理进行自动生成,先看一下实现后的硬编码代码

继承:

代码语言:javascript
复制
public class MovieProxy : Movie
{
	private MoveIntercept _interceptor = new MoveIntercept();
	private Movie _beproxy = new Movie();
	public override void Test()
	{
		object[] parameters = new object[0];
		this._interceptor.Before(this, "Test", parameters);
		base.Test();
		object result = null;
		this._interceptor.After(this, "Test", result);
	}
	public override T Test1<T>()
	{
		object[] parameters = new object[0];
		this._interceptor.Before(this, "Test1", parameters);
		object obj = base.Test1<T>();
		this._interceptor.After(this, "Test1", obj);
		return (T)((object)obj);
	}
}

实现接口

代码语言:javascript
复制
public class MySQLCatchProxy : ICatch
{
	private DefaultIntercept _interceptor = new DefaultIntercept();
	private MySQLCatch _beproxy = new MySQLCatch();
	public override void Catch(string text)
	{
		object[] parameters = new object[]
		{
			text
		};
		this._interceptor.Before(this._beproxy, "Catch", parameters);
		this._beproxy.Catch(text);
		object result = null;
		this._interceptor.After(this._beproxy, "Catch", result);
	}
	public override void UnCatch()
	{
		object[] parameters = new object[0];
		this._interceptor.Before(this._beproxy, "UnCatch", parameters);
		this._beproxy.UnCatch();
		object result = null;
		this._interceptor.After(this._beproxy, "UnCatch", result);
	}
	public override string SaveCatch()
	{
		object[] parameters = new object[0];
		this._interceptor.Before(this._beproxy, "SaveCatch", parameters);
		object obj = this._beproxy.SaveCatch();
		this._interceptor.After(this._beproxy, "SaveCatch", obj);
		return (string)obj;
	}
}

通过反编译动态生成的实现类可以看出,我们要实现的是对需要代理的方法执行前,执行后进行拦截,获取执行对象,方法名,参数等有效信息

对于生成的需要保存的类我们可以如下定义

代码语言:javascript
复制
   string nameOfAssembly = ImpType.Name + "ProxyAssembly";
   string nameOfModule = ImpType.Name + "ProxyModule";
   string nameOfType = ImpType.Name + "Proxy";

   var assemblyName = new AssemblyName(nameOfAssembly);
   ModuleBuilder moduleBuilder = null;
   AssemblyBuilder assemblyBuilder = null;
 assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
   moduleBuilder = assemblyBuilder.DefineDynamicModule(nameOfModule, nameOfType + ".dll");

如果我们只需要用这个类的瞬时定义,定义如下:

代码语言:javascript
复制
 var assembly = AppDomain.CurrentDomain
            .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
 moduleBuilder = assembly.DefineDynamicModule(nameOfModule);

继承方式声明TypeBuilder:

代码语言:javascript
复制
  typeBuilder = moduleBuilder.DefineType(
              nameOfType, TypeAttributes.Public, ImpType);

实现接口方式声明TypeBuilder

代码语言:javascript
复制
 typeBuilder = moduleBuilder.DefineType(
              nameOfType, TypeAttributes.Public, null, new[] { InterfaceType });

如果是继承方式,ImpType与InterfaceType类型一致,声明完这个类,接下来我们就需要对这个类的内容进行完善,先上代码

代码语言:javascript
复制
  private static void InjectInterceptor(TypeBuilder typeBuilder, Type ImpType, Type InterceptType, bool inheritMode, bool isInterceptAllMethod)
        {

            // ---- 变量定义 ----
            var constructorBuilder = typeBuilder.DefineConstructor(
           MethodAttributes.Public, CallingConventions.Standard, null);
            var ilOfCtor = constructorBuilder.GetILGenerator();
            //---- 拦截类对象定义 ----
            //声明
            var fieldInterceptor = typeBuilder.DefineField(
               "_interceptor", InterceptType, FieldAttributes.Private);
            //赋值
            ilOfCtor.Emit(OpCodes.Ldarg_0);
            ilOfCtor.Emit(OpCodes.Newobj, InterceptType.GetConstructor(new Type[0]));
            ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor);

            //---- 实现类对象定义 ----
            //声明
            var fieldBeProxy = typeBuilder.DefineField(
               "_beproxy", ImpType, FieldAttributes.Private);
            //赋值
            ilOfCtor.Emit(OpCodes.Ldarg_0);
            ilOfCtor.Emit(OpCodes.Newobj, ImpType.GetConstructor(new Type[0]));
            ilOfCtor.Emit(OpCodes.Stfld, fieldBeProxy);
            ilOfCtor.Emit(OpCodes.Ret);

            // ---- 定义类中的方法 ----

            var methodsOfType = ImpType.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(a => a.IsVirtual).ToArray();
            string[] ignoreMethodName = new[] { "GetType", "ToString", "GetHashCode", "Equals" };
            for (var i = 0; i < methodsOfType.Length; i++)
            {

                bool IsMethodIntercept = false;
                var method = methodsOfType[i];
                //---- 过滤Object基类方法,如果是继承,基类中的属性get,set方法也要过滤 ----
                if (ignoreMethodName.Contains(method.Name) || method.Name.StartsWith("set_") || method.Name.StartsWith("get_"))
                {
                    continue;
                }

                // ---- 判断方法是否需要拦截 ----
                if (isInterceptAllMethod)
                {
                    if (method.GetCustomAttribute(typeof(IgnoreInterceptAttibute)) == null)
                    {
                        IsMethodIntercept = true;
                    }

                }
                else
                {
                    if (method.GetCustomAttribute(typeof(InterceptAttibute)) != null)
                    {
                        IsMethodIntercept = true;
                    }

                }

                var methodParameterTypes =
                   method.GetParameters().Select(p => p.ParameterType).ToArray();

                // ---- 定义方法名与参数 ----
                var methodBuilder = typeBuilder.DefineMethod(
                  method.Name,
                  MethodAttributes.Public | MethodAttributes.Virtual,
                  CallingConventions.Standard,
                  method.ReturnType,
                  methodParameterTypes);

                //如果是泛型方法
                if (method.IsGenericMethod)
                {
                    //获取所有泛型参数类型定义
                    Type[] Args = method.GetGenericArguments();
                    List<string> GenericArgNames = new List<string>();
                    for (int j = 0; j < Args.Length; j++)
                    {
                        GenericArgNames.Add(Args[j].Name);
                    }
                    //代理类方法生成泛型参数定义
                    GenericTypeParameterBuilder[] DGP = methodBuilder.DefineGenericParameters(GenericArgNames.ToArray());
                    //泛型参数约束设置
                    for (int j = 0; j < DGP.Length; j++)
                    {
                        //泛型参数继承约束
                        DGP[j].SetBaseTypeConstraint(Args[j].BaseType);
                        //泛型参数完成接口约束
                        DGP[j].SetInterfaceConstraints(Args[j].GetInterfaces());
                    }
                    
                }

                var ilOfMethod = methodBuilder.GetILGenerator();
                var methodresult = ilOfMethod.DeclareLocal(typeof(object));  //instance of result
                // ---- before ----
                if (IsMethodIntercept)
                {


                    var parameters = ilOfMethod.DeclareLocal(typeof(object[]));
                    ilOfMethod.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length);
                    ilOfMethod.Emit(OpCodes.Newarr, typeof(object));
                    ilOfMethod.Emit(OpCodes.Stloc, parameters);

                    for (var j = 0; j < methodParameterTypes.Length; j++)
                    {
                        ilOfMethod.Emit(OpCodes.Ldloc, parameters);
                        ilOfMethod.Emit(OpCodes.Ldc_I4, j);
                        ilOfMethod.Emit(OpCodes.Ldarg, j + 1);
                        ilOfMethod.Emit(OpCodes.Stelem_Ref);
                    }


                    ilOfMethod.Emit(OpCodes.Ldarg_0);
                    ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);

                    //拦截方法参数赋值
                    if (inheritMode)
                    {
                        //继承传递代理类本身
                        ilOfMethod.Emit(OpCodes.Ldarg_0);
                    }
                    else
                    {
                        //接口传递实现类
                        ilOfMethod.Emit(OpCodes.Ldarg_0);
                        ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);
                    }

                    ilOfMethod.Emit(OpCodes.Ldstr, method.Name);
                    ilOfMethod.Emit(OpCodes.Ldloc, parameters);
                    //调用拦截类中的Before方法
                    ilOfMethod.Emit(OpCodes.Callvirt, InterceptType.GetMethod("Before"));

                }


                // ---- call ----
                定义实现类局部变量
                //var localimpobj = ilOfMethod.DeclareLocal(ImpType);
                new一个实现类的对象
                //ilOfMethod.Emit(OpCodes.Newobj, ImpType.GetConstructor(new Type[0]));
                局部变量赋值
                //ilOfMethod.Emit(OpCodes.Stloc, localimpobj);

                局部变量出栈,等待调用
                //ilOfMethod.Emit(OpCodes.Ldloc, localimpobj);


                if (inheritMode)
                {
                    //继承方法调用父类的方法
                    ilOfMethod.Emit(OpCodes.Ldarg_0);
                    ilOfMethod.Emit(OpCodes.Call, ImpType.GetMethod(method.Name));
                }
                else
                {
                    //接口方法调用实现类的方法
                    ilOfMethod.Emit(OpCodes.Ldarg_0);
                    ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);
                    for (var j = 0; j < methodParameterTypes.Length; j++)
                    {
                        ilOfMethod.Emit(OpCodes.Ldarg, j + 1);
                    }
                    //调用方法
                    ilOfMethod.Emit(OpCodes.Callvirt, ImpType.GetMethod(method.Name));
                }


                // ---- after ----
                if (IsMethodIntercept)
                {
                    if (method.ReturnType == typeof(void))
                    {
                        ilOfMethod.Emit(OpCodes.Ldnull);
                    }
                    else
                    {
                        ilOfMethod.Emit(OpCodes.Box, method.ReturnType);
                    }

                    ilOfMethod.Emit(OpCodes.Stloc, methodresult);

                    ilOfMethod.Emit(OpCodes.Ldarg_0);
                    ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);


                    //拦截方法参数赋值
                    if (inheritMode)
                    {
                        //继承传递代理类本身
                        ilOfMethod.Emit(OpCodes.Ldarg_0);
                    }
                    else
                    {
                        //接口传递实现类
                        ilOfMethod.Emit(OpCodes.Ldarg_0);
                        ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);
                    }

                    ilOfMethod.Emit(OpCodes.Ldstr, method.Name);
                    ilOfMethod.Emit(OpCodes.Ldloc, methodresult);
                    ilOfMethod.Emit(OpCodes.Callvirt, InterceptType.GetMethod("After"));

                    //调用完After方法后,将实现类的方法返回值的临时变量再次压栈用作代理方法的返回
                    ilOfMethod.Emit(OpCodes.Ldloc, methodresult);

                }

                // pop the stack if return void
                if (method.ReturnType == typeof(void))
                {
                    ilOfMethod.Emit(OpCodes.Pop);
                }
                else
                {
                    if (method.ReturnType.IsValueType)
                    {
                        ilOfMethod.Emit(OpCodes.Unbox_Any, method.ReturnType);
                    }
                    else
                    {
                        ilOfMethod.Emit(OpCodes.Castclass, method.ReturnType);
                    }
                }


                // complete
                ilOfMethod.Emit(OpCodes.Ret);
            }
        }

代码可以有点长,我们慢慢分析

1.我们首先通过

代码语言:javascript
复制
 ilOfCtor.Emit(OpCodes.Ldarg_0);
 ilOfCtor.Emit(OpCodes.Newobj, InterceptType.GetConstructor(new Type[0]));
 ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor);

这种方式,定义全局变量,也就是生成类中的代理类,和实现类对象

代码语言:javascript
复制
private DefaultIntercept _interceptor = new DefaultIntercept();
private MySQLCatch _beproxy = new MySQLCatch();

2.定义方法,我们获取类中的方法会包含GetType,ToString,GetHashCode,Equals这四个Object类中的方法,需要过滤,如果是继承方法是,属性的set,get方法也要去除,如果方法是泛型方法,我们需要对泛型的数量,名称,泛型约束进行同步

代码语言:javascript
复制
                //如果是泛型方法
                if (method.IsGenericMethod)
                {
                    //获取所有泛型参数类型定义
                    Type[] Args = method.GetGenericArguments();
                    List<string> GenericArgNames = new List<string>();
                    for (int j = 0; j < Args.Length; j++)
                    {
                        GenericArgNames.Add(Args[j].Name);
                    }
                    //代理类方法生成泛型参数定义
                    GenericTypeParameterBuilder[] DGP = methodBuilder.DefineGenericParameters(GenericArgNames.ToArray());
                    //泛型参数约束设置
                    for (int j = 0; j < DGP.Length; j++)
                    {
                        //泛型参数继承约束
                        DGP[j].SetBaseTypeConstraint(Args[j].BaseType);
                        //泛型参数完成接口约束
                        DGP[j].SetInterfaceConstraints(Args[j].GetInterfaces());
                    }
                    
                }

3.方法声明完毕后,我们需要进行方法的内部实现,实现顺序为,Before——Invoke——After,我这里的代码加了一些判断,是结合Attribue使用,有两种方式,如果是全局拦截,则方法有IgnoreInterceptAttibute标记的不拦截,如果是自定义拦截,则拦截有InterceptAttibute标记的方法,使用更加方便灵活,在调用Before和After方法时,对于传入的引用对象需要进行区分,如果是继承方法,通过 ilOfMethod.Emit(OpCodes.Ldarg_0);传入this,即子类本身,如果是实现接口方法,通过 ilOfMethod.Emit(OpCodes.Ldarg_0);ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);加载声明的接口实现,对于有返回值的结果,我们需要对Invoke执行的结果出栈  ilOfMethod.Emit(OpCodes.Stloc, methodresult);,但是动态代理类本身也要返回值,所以调用完After方法后需要通过ilOfMethod.Emit(OpCodes.Ldloc, methodresult);对返回值再次入栈,需要注意的是,继承方式拦截,父类方法一定要声明为虚方法,才能被子类重写

大概实现过程已经分析完毕了,对于Emit最重要的还是OpCodes的理解和方法调用与栈之间的联系,如果有什么不完善的地方,希望大家提出意见,相互交流,共同进步!

完整源码地址:FastIOC: 轻量级IOC容器 中的DynamictProxy类

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档