Emit学习之旅(5):创建动态代理- Frog - 博客园

  有这样的场景:我们已经完成了一套系统,但是客户提出要求,在进行重要操作时要记录日志。这时该如何应对?

 

  一.直接修改源代码

  拿到需求不加思索,OK,不就是记录日志吗,添加一个写日志的类,然后在重要操作中调用它的方法。

  二.动态代理

  方法1固然可以达到目的,却十分丑陋。先不说这样违背了基本的OO原则(对扩展开放,对修改封闭),当要记录日志的操作数量庞大时,就算Ctrl-c,Ctrl-V估计也得弄到手软。

  但这个时候恰好是动态代理发挥作用的时候。

  假设Subject包含了一个需要记录操作日志的方法Process:

public class Subject
{
    
public virtual void Process()
    {
        Console.WriteLine(
"Processing");
    }
}

 

  在摒弃了方法1后,我们可以这样来考虑,继承Subject并重写Process方法

public class Proxy : Subject
{
    
public override void Process()
    {
        
//TODO:Write Log
        base.Process();
    }
}

 

  不过相比上面的方式,下面这种显得更优雅:

public class Proxy : Subject
{
    
private Interceptor _interceptor;
    
public Proxy()
    {
        _interceptor 
= new Interceptor();
    }
    
public override void Process()
    {
        _interceptor.Invoke(
new Subject(), "Process"null);
    }
}
public class Interceptor
{
    
public object Invoke(object obj, string methodName, object[] parameters)
    {
        Console.WriteLine(
string.Format("before invoke {0}...", methodName));
        var retObj
= obj.GetType().GetMethod(methodName).Invoke(obj, parameters);
        Console.WriteLine(
string.Format("after invoke {0}...", methodName));
        
return retObj;
    }
}

 

  这样我们就可以将记录日志的动作全部移入Interceptor。

  当然,如果所有的代理都需要手动去编写,工作量可想而知。但上面说了,这是动态代理擅长的领域。我们可以在运行时动态生成这些代理。下面就来说说如何用Emit动态生成这些代理。

  其实,写到这个地方,思路已经很清晰了,我们只需要将上面那段代码里实现代理的代码换成用Emit来实现就可以了。

  1.定义一个创建代理的类:

public class ProxyCreator<T> 
    
where T: class,new()
{
    
public static T CreateProxy()
    {
    ......
  }
}

 

  2.接着进入关键部分:实现CreateProxy方法

  (1).创建代理类:

var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Proxy"), AssemblyBuilderAccess.RunAndSave);
var moduleBldr 
= asm.DefineDynamicModule("Main""Proxy.dll");
var typeBldr 
= moduleBldr.DefineType(typeof(T).Name + "Proxy", TypeAttributes.Public, typeof(T));

 

  (2).创建_interceptor实例变量

//interceptor
var fldInterceptor = typeBldr.DefineField("interceptor"typeof(Interceptor), FieldAttributes.Private);

 

  (3).创建构造函数

//construtor
var constructorBldr = typeBldr.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
var il 
= constructorBldr.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);

il.Emit(OpCodes.Newobj, 
typeof(Interceptor).GetConstructor(new Type[0]));
il.Emit(OpCodes.Stfld, fldInterceptor);
il.Emit(OpCodes.Ret);

 

  (4).覆写需要记录操作日志的方法(这里选取了所有的public实例方法)

        //methods
        var methods = typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance);

        
for (var i = 0; i < methods.Length; i++)
        {
            var paramTypes 
= GetParametersType(methods[i]);
            var methodBlfr 
= typeBldr.DefineMethod(methods[i].Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard, methods[i].ReturnType, paramTypes);

            il 
= methodBlfr.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, fldInterceptor);
            il.Emit(OpCodes.Newobj, 
typeof(T).GetConstructor(new Type[0]));
            il.Emit(OpCodes.Ldstr, methods[i].Name);
            
//创建参数数组
            if (paramTypes == null)
            {
                il.Emit(OpCodes.Ldnull);
            }
            
else
            {
                var parameters 
= il.DeclareLocal(typeof(object[]));
                il.Emit(OpCodes.Ldc_I4, paramTypes.Length);
                il.Emit(OpCodes.Newarr, 
typeof(object));
                il.Emit(OpCodes.Stloc, parameters);

                
for (var j = 0; j < paramTypes.Length; j++)
                {
                    il.Emit(OpCodes.Ldloc, parameters);
                    il.Emit(OpCodes.Ldc_I4, j);
                    il.Emit(OpCodes.Ldarg, j 
+ 1);

                    il.Emit(OpCodes.Stelem_Ref);
                }
                il.Emit(OpCodes.Ldloc, parameters);
            }
            il.Emit(OpCodes.Callvirt, 
typeof(Interceptor).GetMethod("Invoke"));
            
if (methods[i].ReturnType == typeof(void))
            {
                il.Emit(OpCodes.Pop);
            }
            il.Emit(OpCodes.Ret);
        }

 

  (5).完成类型创建

        var t = typeBldr.CreateType();
        asm.Save(
"Proxy.dll");
        
return Activator.CreateInstance(t) as T;

 

  这样级基本完成了一个创建动态代理的工具类。接下来就来试试吧:

class Program
{
    
static void Main(string[] args)
    {
        var p 
= ProxyCreator<Subject>.CreateProxy();
        p.Process();

    }
}

 

   结果如下图所示:
示例完整代码
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;


public class ProxyCreator<T> 
    
where T: class,new()
{
    
public static T CreateProxy()
    {
        var asm 
= AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Proxy"), AssemblyBuilderAccess.RunAndSave);

        var moduleBldr 
= asm.DefineDynamicModule("Main""Proxy.dll");

        var typeBldr 
= moduleBldr.DefineType(typeof(T).Name + "Proxy", TypeAttributes.Public, typeof(T));
        
//interceptor
        var fldInterceptor = typeBldr.DefineField("interceptor"typeof(Interceptor), FieldAttributes.Private);
        
//construtor
        var constructorBldr = typeBldr.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
        var il 
= constructorBldr.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);

        il.Emit(OpCodes.Newobj, 
typeof(Interceptor).GetConstructor(new Type[0]));
        il.Emit(OpCodes.Stfld, fldInterceptor);
        il.Emit(OpCodes.Ret);

        
//methods
        var methods = typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance);

        
for (var i = 0; i < methods.Length; i++)
        {
            var paramTypes 
= GetParametersType(methods[i]);
            var methodBlfr 
= typeBldr.DefineMethod(methods[i].Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard, methods[i].ReturnType, paramTypes);

            il 
= methodBlfr.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, fldInterceptor);
            il.Emit(OpCodes.Newobj, 
typeof(T).GetConstructor(new Type[0]));
            il.Emit(OpCodes.Ldstr, methods[i].Name);
            
//创建参数数组
            if (paramTypes == null)
            {
                il.Emit(OpCodes.Ldnull);
            }
            
else
            {
                var parameters 
= il.DeclareLocal(typeof(object[]));
                il.Emit(OpCodes.Ldc_I4, paramTypes.Length);
                il.Emit(OpCodes.Newarr, 
typeof(object));
                il.Emit(OpCodes.Stloc, parameters);

                
for (var j = 0; j < paramTypes.Length; j++)
                {
                    il.Emit(OpCodes.Ldloc, parameters);
                    il.Emit(OpCodes.Ldc_I4, j);
                    il.Emit(OpCodes.Ldarg, j 
+ 1);

                    il.Emit(OpCodes.Stelem_Ref);
                }
                il.Emit(OpCodes.Ldloc, parameters);
            }
            il.Emit(OpCodes.Callvirt, 
typeof(Interceptor).GetMethod("Invoke"));
            
if (methods[i].ReturnType == typeof(void))
            {
                il.Emit(OpCodes.Pop);
            }
            il.Emit(OpCodes.Ret);
        }
        var t 
= typeBldr.CreateType();
        asm.Save(
"Proxy.dll");
        
return Activator.CreateInstance(t) as T;
    }
    
private static Type[] GetParametersType(MethodInfo method)
    {
        Type[] paramTypes 
= null;

        
if (method != null)
        {
            var parameters 
= method.GetParameters();
            
if (parameters.Length > 0)
            {

                paramTypes 
= new Type[parameters.Length];

                
for (var i = 0; i < parameters.Length; i++)
                {
                    paramTypes[i] 
= parameters[i].ParameterType;
                }
            }
        }
        
return paramTypes;
    }
}
class Program
{
    
static void Main(string[] args)
    {
        var p 
= ProxyCreator<Subject>.CreateProxy();
        p.Process();

    }
}


public class Subject
{
    
public virtual void Process()
    {
        Console.WriteLine(
"Processing");
    }
}

public class Interceptor
{
    
public object Invoke(object obj, string methodName, object[] parameters)
    {
        Console.WriteLine(
string.Format("before invoke {0}...", methodName));
        var retObj
= obj.GetType().GetMethod(methodName).Invoke(obj, parameters);
        Console.WriteLine(
string.Format("after invoke {0}...", methodName));
        
return retObj;
    }
}

 

    谈到动态代理就会想到AOP,其实上面就是一个简单的AOP实现。这也是我学习Emit的初衷。鉴于对AOP认识不多,这里就不讨论了,有机会再share学习心得,也恳请大家为小弟推荐些学习AOP的资源。

   

 

 


郑重声明:资讯 【Emit学习之旅(5):创建动态代理- Frog - 博客园】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——