四、在MSIL世界建立起强大的AOP帝国
既然能用C#直接写MSIL,那么就可容易编写AOP框架了。虽然这是用C#代码写MSIL,但也要对MSIL有一定的了解,感兴趣的读者可以到微软网站去下载IL Specification。
由于这个AOP框架的代码十分庞大,在这里只给出了一些代码片段。实现AOP框架的核心就是生成动态代理类。因此,使用IL生成代理类的框架是第一步。下面是生成代理类框架的核心代码:
从上面的代码很容易猜到我要生成一个public的sealed类(不可继承)。接下来就是根据父类生成相应的方法(包括普通方法和构造方法),下面是一些代码片段:public TypeBuilder GenerateType() // 返回动态代理类的Type ...{ string className = GetNewClassName(); TypeAttributes typeAttributes = TypeAttributes.Class | TypeAttributes.Public |
TypeAttributes.Sealed; TypeBuilder typeBuilder = m_EmitClassInfo.Module.DefineType(className, typeAttributes, m_EmitClassInfo.BaseType); return typeBuilder; }
private void GenerateMethod() // 生成普通的方法 ...{ MethodAttributes methodAttributes = MethodAttributes.Public; MethodBuilder methodBuilder = m_TypeBuilder.DefineMethod("__GetMethodInvocation", methodAttributes, typeof(IMethodInvocation),
new Type[] ...{ typeof(ICallable), typeof(MethodInfo) }); m_EmitClassInfo.__GetMethodInvocation = methodBuilder; ILGenerator ilGenerator = methodBuilder.GetILGenerator(); Label execIfLabel = ilGenerator.DefineLabel(); Label endIfLabel = ilGenerator.DefineLabel(); LocalBuilder methodInvocation = ilGenerator.DeclareLocal(typeof(IMethodInvocation)); ilGenerator.Emit(OpCodes.Ldarg_0); ... ... }
使用Emit不仅仅可以实现AOP框架,还可以根据用户需要自动生成任何IL代码。这些IL代码不会受到VB.net、C#的限制。因此,用户可以通过Emit来优化生成的中间语言代码,并完成一些C#无法做到的任务。如果读者想了解Emit的详细用法,可以参考MSDN或其他的相关文档。private void GenerateConstructor() // 生成构造方法 ...{ try ...{ MethodAttributes methodAttributes = MethodAttributes.Public; CallingConventions callingConventions = CallingConventions.Standard; m_BaseConstructorParams = m_EmitClassInfo.ConstructorArgumentsType; m_ConstructorParams = new Type[m_BaseConstructorParams.Length + 1]; m_BaseConstructorParams.CopyTo(m_ConstructorParams, 0); m_ConstructorParams[m_BaseConstructorParams.Length] = typeof(IInterceptor); m_constructorBuilder = m_TypeBuilder.DefineConstructor(methodAttributes, callingConventions,
m_ConstructorParams); m_EmitClassInfo.Constructor = m_constructorBuilder; m_IlGenerator = m_constructorBuilder.GetILGenerator(); } catch (Exception e) ...{ throw e; } }