象DotNet,Java之类的语言能够进行动态代理类的创建,得益于其本身并不是直接编译成机器代码,而是编译成中间语言,在运行时才解释或动态编译成目标机器语言。这也是为什么这些概念先在Java兴起的根本原因。产生动态代理类,一般都是利用Emit命名空间的指令,但这个对IL的要求比较高,我这里利用C#提供的动态编译功能实现,
优点是直观,容易理解,不用熟悉IL指令,缺点当然是显得不怎么专业。
(网上很多利用Emit,IL指令构建动态代理类的代码)
能够动态代理(我更倾向于用装饰),一个很关键的地方就是要求你的类中需要被监视(hook)的成员至少是保
护的虚方法.
下面就是动态代理类的工厂类,这里先介绍这个类,后面的博文会解释为什么要这么做:
//代理类工厂,简单的工厂方法.
public class MyProxyFactory { object _target; private List<IMethodInterceptor> _adviceList = new List<IMethodInterceptor>(); //所谓的前置,后置等通知性类,其实就是要注入的间谍。 public void Add(IMethodInterceptor advice) { _adviceList.Add(advice); } //构造函数,传入目标对象. public MyProxyFactory(object target) { _target = target; } // 创建并放回代理类。注意,这里是每次都创建,实际上是可以缓存的. public object GetProxyClass() { StringBuilder theClassBuilder = new StringBuilder(); Type theTargetType = _target.GetType(); string theProxyClassName = "__" + theTargetType.Name + "_Proxy"; theClassBuilder.AppendLine("using System;"); theClassBuilder.AppendLine("using System.Collections.Generic;"); theClassBuilder.AppendLine("using System.Text;"); theClassBuilder.AppendLine("using System.Reflection;"); theClassBuilder.AppendLine("using System.Reflection.Emit;"); theClassBuilder.AppendLine("namespace " + theTargetType.Namespace); theClassBuilder.AppendLine("{"); theClassBuilder.AppendLine("public class "+theProxyClassName+" : " + theTargetType.FullName + "{"); theClassBuilder.AppendLine(" private " + theTargetType.FullName + " _target;"); theClassBuilder.AppendLine(" private List<IMethodInterceptor> _adviceList = null;"); theClassBuilder.AppendLine(" public __" + theTargetType.Name + "_Proxy(" + theTargetType.FullName + " target, List<IMethodInterceptor> AdviceList)"); theClassBuilder.AppendLine(" {"); theClassBuilder.AppendLine(" _target = target;"); theClassBuilder.AppendLine(" this._adviceList = AdviceList;"); theClassBuilder.AppendLine(" }"); //获取目标类的方法,准备给代理类继承 MethodInfo[] methodinfos = theTargetType.GetMethods(BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance); foreach (MethodInfo mi in methodinfos) { if (mi.IsVirtual) { string theRetType = mi.ReturnType.FullName; if (mi.ReturnType == typeof(void)) { theRetType = "void"; } string theParams = ""; string theTypes = ""; string thePMInsts = ""; ParameterInfo[] thePs = mi.GetParameters(); for (int i = 0; i < thePs.Length; i++) { if (i == 0) { theTypes = "typeof(" + thePs[i].ParameterType.FullName + ")"; if (thePs[i].IsOut) { theParams = "out " + thePs[i].ParameterType.FullName + " " + thePs[i].Name; thePMInsts = "out " + thePs[i].Name; } else { theParams = "" + thePs[i].ParameterType.FullName + " " + thePs[i].Name; thePMInsts = "" + thePs[i].Name; } } else { theTypes = ",typeof(" + thePs[i].ParameterType.FullName + ")"; if (thePs[i].IsOut) { thePMInsts = ",out " + thePs[i].Name; theParams = ",out " + thePs[i].ParameterType.FullName + " " + thePs[i].Name; } else { thePMInsts = "," + thePs[i].Name; theParams = "," + thePs[i].ParameterType.FullName + " " + thePs[i].Name; } } } theClassBuilder.AppendLine(" public override " + theRetType + " " + mi.Name + "(" + theParams + ")"); theClassBuilder.AppendLine(" {"); theClassBuilder.AppendLine(" if (_adviceList != null)"); theClassBuilder.AppendLine(" {"); theClassBuilder.AppendLine(" foreach (IMethodInterceptor item in _adviceList)"); theClassBuilder.AppendLine(" {"); theClassBuilder.AppendLine(" MethodInfo theMI = _target.GetType().GetMethod(\"" + mi.Name + "\", new Type[] { " + theTypes + "});"); theClassBuilder.AppendLine(" AdviseInvocation theInvocation = new AdviseInvocation(_target, theMI, new object[]{" + thePMInsts + "});"); if (mi.ReturnType == typeof(void)) { theClassBuilder.AppendLine(" item.Invoke(theInvocation);"); } else { theClassBuilder.AppendLine(" return (" + theRetType + ")(item.Invoke(theInvocation));"); } theClassBuilder.AppendLine(" }"); theClassBuilder.AppendLine(" }"); if (mi.ReturnType == typeof(void)) { theClassBuilder.AppendLine(" _target." + mi.Name + "(" + thePMInsts + ");"); } else { theClassBuilder.AppendLine(" return (" + theRetType + ")(_target." + mi.Name + "(" + thePMInsts + "));"); } theClassBuilder.AppendLine("}"); } } theClassBuilder.AppendLine("}"); theClassBuilder.AppendLine("}"); //测试观察用 Console.WriteLine(theClassBuilder.ToString()); //===============================动态编译代理类代码==================================== //创建编译器实例。 CSharpCodeProvider provider = new CSharpCodeProvider(); //设置编译参数。 CompilerParameters paras = new CompilerParameters(); paras.GenerateExecutable = false; paras.GenerateInMemory = true; //paras.ReferencedAssemblies.Add("System.dll"); paras.ReferencedAssemblies.Add("SpringNetStudy.exe"); //paras.ReferencedAssemblies.Add("mscorlib.dll"); //paras.ReferencedAssemblies.Add(""); //编译代码。 CompilerResults result = provider.CompileAssemblyFromSource(paras, theClassBuilder.ToString()); //获取编译后的程序集。 Assembly assembly = result.CompiledAssembly; Type theType = assembly.GetType(theTargetType.Namespace+"."+ theProxyClassName,false,true); var obj = Activator.CreateInstance(theType, new object[] {_target,_adviceList}); return obj; } }后记:有些东西看起来很神秘,但实际上也不过如此。国内没法做出好的框架,其实一个主要的原因就是编译技术不过关,没有自己的语言。如果Emit和动态编译命名空间的方法不公开,就很难做了.