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

2023-07-19 14:36:32 浏览数 (1)

之前自己在写一个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类

0 人点赞