在轻量级AOP框架-移植python装饰器(Decorator)到C#(研究篇)中文章分析了Python中Decorator原理以及C#移植可行性在本篇中文章将继续探讨如何将这个想法实实在在表现出来因此本篇目标是:个初级但是可用Decorator实现
如果您对本文基本思路存在疑惑请先阅读研究篇
2. 实现分析
上篇中我们考虑实现个Wrapper类来做到模仿Python替换功能然而在实际使用中如果靠人工书写很显然是个不切实际想法因此框架关键在于对被装饰思路方法处理当前我们般使用动态代理或者静态织入方式进行该操作然而无论是哪种思路方法关键点都在于对现有代码“动态修改”(动态代理修改在于运行时静态织入修改在于编译时)
在本篇中我们考虑个动态代理实现具体运作方式如下:
1. 运行时采用框架中工厂生成代理对象即:框架中工厂思路方法传入欲生成对象类型因此对象创建方式将发生改变:默认情况下我们可能采用 var testClass = TestClass;方式生成对象在使用代理情况下必须强制使用var testClass = xxx.CreateInstanse<TestClass>;方式生成对象
2. 框架工厂类获取到对象类型的后检查对象是否为可继承对象如果不是则无法生成代理类否则进行下步
3. 动态类生产引擎生成TestClassWrapper类,并从TestClass继承
4. 采用定方式重写TestClass中欲进行处理思路方法以满足上篇中预设结果
5. 生成TestClassWrapper类例子并返回
3. 编码难点
在了解了具体运作方式的后我们可以分别考虑各个步骤实现难点第和第 2都不难使用基本反射即可实现主要问题在于3-5步下面我们分别对这几步实现进行编码难点分析
对于第 3步类继承很显然这首先要具备个条件那就是原始类是可继承否则也无从谈起TestClassWrapper生成如果满足条件那么可以使用反射创建动态类同时在c#中我们需要创建个动态集来容纳这些动态类
对于第 4步系统需要重写欲处理思路方法要达到这个目标我们只能请出我们终极大神MSIL了在C#中可以使用Emit方式进行IL嵌入编程虽然麻烦了点但这总算也能高效解决问题
对于第 5步般来说可以Activator.CreateInstance思路方法来创建对象然而在本人另篇博客探究.net对象创建, 质疑再谈Activator.CreateInstance(Type type)思路方法创建对象和Expression Tree创建对象性能比较中我们可以了解到Activator.CreateInstance并不是最高效做法因此框架将考虑使用更高效方式如Emit或者ExpressionTree方式生成委托并构造对象
4. 具体编码
在编码前为了方便快速构造框架个人将些成熟代码放入框架中以提高效率这部分代码主要有:FastReflection可以通过Emit快速构造思路方法委托CodeGeneratorNbear框架中个IL编程帮助类通过它可以让我们简化不少IL操作
现在万事俱备我们开始步步创造框架吧首先定义DecoratorFilter接口以方便扩展Attribute同时为了方便我们也定义个DecoratorContexe来取代上篇中直接传入Wrapper思路方法委托对象DecoratorFilter接口定义如下:
01 public erface IDecoratorFilter {
02 Func<object, object, object> Execute(DecoratorContext context);
03 }
04 其中DecoratorContext类包含了欲封装思路方法基本属性其定义如下:
05 public sealed DecoratorContext {
06 /// <summary>
07 /// 该思路方法通用委托
08 /// </summary>
09 public Func<object, object, object> Invoker { get; private ; }
10
11 /// <summary>
12 /// this对象
13 /// </summary>
14 public object Instanse { get; private ; }
15
16 /// <summary>
17 /// 思路方法参数列表
18 /// </summary>
19 public object Parameters { get; private ; }
20
21 public DecoratorContext(Func<object, object, object> invoker, object instanse, object parameters) {
22 Invoker = invoker;
23 Instanse = instanse;
24 Parameters = parameters;
25 }
26 }
然后需要完成是DynamicTypeBuilder类该类负责动态集动态类型动态思路方法创建具体实现如下:
01 ernal DynamicTypeBuilder {
02 //为动态集生成名称
03 private readonly assemblyName = Guid.NewGuid.;
04 //集主模块
05 private readonly ModuleName = "Main";
06
07 public AssemblyBuilder AssemblyBuilder { get; private ; }
08
09 public ModuleBuilder MainModuleBuilder { get; private ; }
10
11 public DynamicTypeBuilder {
12 AssemblyName an = AssemblyName(assemblyName);
13 //构造个可运行动态集对象
14 AssemblyBuilder = AppDo.CurrentDo.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
15 //为该集构造主模块
16 MainModuleBuilder = AssemblyBuilder.DefineDynamicModule(ModuleName);
17 }
18
19 /// <summary>
20 /// 从Type类派生个新代理类
21 /// </summary>
22 /// <param name="type">基类</param>
23 /// <s></s>
24 public TypeBuilder CreateTypeBuilder(Type type) {
25 TypeBuilder typeBuilder = MainModuleBuilder.DefineType(type.Name + "_" + Guid.NewGuid.,
26 TypeAttributes.Class | TypeAttributes.Public,
27 type);
28 typeBuilder;
29 }
30
31 /// <summary>
32 /// 根据给定思路方法对象和类型对象构造CodeGenerator
33 /// </summary>
34 /// <param name="typeBuilder"></param>
35 /// <param name="method"></param>
36 /// <s></s>
37 public CodeGenerator CreateMethodCodeGenerator(TypeBuilder typeBuilder, MethodInfo method) {
38 CodeGenerator cg = CodeGenerator(typeBuilder,
39 method.Name,
40 MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.HideBySig,
41 CallingConventions.HasThis,
42 method.ReturnType,
43 method.GetParameters.Select(e => e.ParameterType).ToArray);
44 typeBuilder.DefineMethodOverride(cg.CurrentMethod, method);
45 cg;
46 }
47 }
为了提高框架运行效率系统也构造个简单缓存Cache系统将缓存Cache些元数据详细情况可以参考完整源文件
最后考虑最麻烦TypeFactory类该类中最重要思路方法就是CreateType它可以使说框架中最核心部分了通过该该思路方法我们创建了个新Class该Class中完成了Decorator核心实现下面给出该思路方法实现该类完整实现也请参考源码
001 public Type CreateType(Type rawType) {
002 var typeBuilder = builder.CreateTypeBuilder(rawType);
003 MetaCacheItem metaCacheItem = MetaCache.Get(rawType);
004 MethodInfo getTypeMethodInfo = rawType.GetMethod("GetType");
005 foreach (var item in metaCacheItem.Methods) {
006 (item.Method.IsVirtual && item.Filters != null && item.Filters.Length > 0) {
007 var cg = builder.CreateMethodCodeGenerator(typeBuilder, item.Method);
008 var parameters = item.Method.GetParameters;
009 var cmpLabel = cg.DefineLabel;
010 var loopLabel = cg.DefineLabel;
011 //Type type;
012 var typeLocal = cg.DeclareLocal(typeType);
013 //MethodInfo thisMethodInfo;
014 var thisMethodInfoLocal = cg.DeclareLocal(methodInfoType);
015 //Type typeParameters;
016 var typeParametersLocal = cg.DeclareLocal(typeArrayType);
017 //MetaCacheItem metaCacheItem;
018 var metaCacheItemLocal = cg.DeclareLocal(metaCacheItemType);
019 //MetaMethodInfo metaMethodInfo;
020 var metaMethodInfoLocal = cg.DeclareLocal(metaMethodInfoType);
021 //Func<object, object, object> fun;
022 var funLocal = cg.DeclareLocal(genericInvokerType);
023 //object parameters;
024 var parametersLocal = cg.DeclareLocal(objectArrayType);
025 //IDecoratorFilter filters;
026 var filtersLocal = cg.DeclareLocal(iDecoratorFilterArrayType);
027 //IDecoratorFilter item;
028 var itemLocal = cg.DeclareLocal(iDecoratorFilterType);
029 // num;
030 var numLocal = cg.DeclareLocal(32Type);
031 //type = this.GetType;
032 //cg.Ldarg(0);
033 //cg.Call(getTypeMethodInfo);
034 cg.Ldtoken(rawType);
035 cg.Call(getTypeFromHandleMethodInfo);
036 cg.Stloc(typeLocal);
037 //typeParameters = Type { xxx };
038 cg.NewArray(typeType, parameters.Length);
039 cg.Stloc(typeParametersLocal);
040 for ( i = 0; i < parameters.Length; i) {
041 cg.Ldloc(typeParametersLocal);
042 cg.Ldc(i);
043 cg.Ldtoken(parameters[i].ParameterType);
044 cg.Call(getTypeFromHandleMethodInfo);
045 //cg.Ldarg(i + 1);
046 //cg.Call(getTypeMethodInfo);
047 cg.Stelem(typeType);
048 }
049 //thisMethodInfo = type.GetMethod("xxx", typeParameters);
050 cg.Ldloc(typeLocal);
051 cg.Ldstr(item.Method.Name);
052 cg.Ldloc(typeParametersLocal);
053 cg.Call(getMethodMethodInfo);
054 cg.Stloc(thisMethodInfoLocal);
055 //metaCacheItem = MetaCache.Get(type);
056 cg.Ldloc(typeLocal);
057 cg.Call(metaCacheGetMethodInfo);
058 cg.Stloc(metaCacheItemLocal);
059 //metaMethodInfo = TypeFactory.FindMetaMethodInfo(metaCacheItem.Methods, thisMethod);
060 cg.Ldloc(metaCacheItemLocal);
061 cg.Call(metaCacheItemMethodsGetMethodInfo);
062 cg.Ldloc(thisMethodInfoLocal);
063 cg.Call(findMetaMethodInfoMethodInfo);
064 cg.Stloc(metaMethodInfoLocal);
065 //fun = TypeFactory.CreateGenericInvoker(metaMethodInfo.Method);
066 cg.Ldloc(metaMethodInfoLocal);
067 cg.Call(metaMethodInfoMethodGetMethodInfo);
068 cg.Call(createGenericInvokerMethodInfo);
069 cg.Stloc(funLocal);
070 //parameters = object { xxx };
071 cg.NewArray(objectType, parameters.Length);
072 cg.Stloc(parametersLocal);
073 for ( i = 0; i < parameters.Length; i) {
074 cg.Ldloc(parametersLocal);
075 cg.Ldc(i);
076 cg.Ldarg(i + 1);
077 (parameters[i].ParameterType.IsValueType) {
078 cg.Box(parameters[i].ParameterType);
079 }
080 cg.Stelem(typeType);
081 }
082 cg.Ldloc(metaMethodInfoLocal);
083 cg.Call(metaMethodInfoFiltersGetMethodInfo);
084 cg.Stloc(filtersLocal);
085 //开始循环
086 cg.Ldc(0);
087 cg.Stloc(numLocal);
088 cg.Br(cmpLabel);
089 cg.MarkLabel(loopLabel);
090 cg.Ldloc(filtersLocal);
091 cg.Ldloc(numLocal);
092 cg.Ldelem(iDecoratorFilterType);
093 cg.Stloc(itemLocal);
094 //loop
095 //item.Execute( DecoratorContext(fun, this, parameters));
096 cg.Ldloc(itemLocal);
097 cg.Ldloc(funLocal);
098 cg.Ldarg(0);
099 cg.Ldloc(parametersLocal);
100 cg.New(decoratorContextConstructorInfo);
101 cg.Call(iDecoratorFilterExecuteMethodInfo);
102 cg.Stloc(funLocal);
103 //endloop
104 cg.Ldloc(numLocal);
105 cg.Ldc(1);
106 cg.Add;
107 cg.Stloc(numLocal);
108 cg.MarkLabel(cmpLabel);
109 cg.Ldloc(numLocal);
110 cg.Ldloc(filtersLocal);
111 cg.Ldlen;
112 cg.Blt(loopLabel);
113 // ()fun(this, parameters)
114 cg.Ldloc(funLocal);
115 cg.Ldarg(0);
116 cg.Ldloc(parametersLocal);
117 cg.Call(genericInvokeInvokerMethodInfo);
118 (item.Method.ReturnType != voidType && item.Method.ReturnType != objectType) {
119 cg.ConvertValue(objectType, item.Method.ReturnType);
120 } (item.Method.ReturnType voidType) {
121 cg.Pop;
122 }
123 cg.Ret;
124 }
125 }
126 typeBuilder.CreateType;
127 }
将这部分IL翻译下,如果我们定义了个思路方法public virtual Test( p)那么自动生成思路方法大致会像下面样
01 public override Test( p) {
02 Type type = typeof(TestClass);
03 MethodInfo thisMethod = type.GetMethod("Test", Type { typeof() });
04 MetaCacheItem metaCacheItem = MetaCache.Get(type);
05 MetaMethodInfo thisMetaMethod = metaCacheItem.Methods.FirstOrDefault(e => e.Method thisMethod);
06 Func<object, object, object> fun = TypeFactory.CreateGenericInvoker(thisMetaMethod.Method);
07 object parameters = object { p };
08 foreach (var item in thisMetaMethod.Filters) {
09 DecoratorContext context = DecoratorContext(fun, this, parameters);
10 fun = item.Execute(context);
11 }
12 ()fun(this, parameters);
13 }
5. 框架测试
首先测试功能我们仍然以上篇中第个Python代码为例想办法达到样效果准备代码如下:
01 /// <summary>
02 /// 和@logger功能致LoggerAttribute实现
03 /// </summary>
04 public LoggerAttribute : Attribute, IDecoratorFilter {
05 public Name { get; private ; }
06
07 public LoggerAttribute( name) {
08 Name = name;
09 }
10
11 #region IDecoratorFilter Members
12
13 public Func<object, object, object> Execute(DecoratorContext context) {
14 (instanse, parameters) => {
15 Console.WriteLine("User is {0}.", Name);
16 Console.WriteLine("Start Logging.");
17 var result = context.Invoker(instanse, parameters);
18 Console.WriteLine("End Logging.");
19 result;
20 };
21 }
22
23 #endregion
24 }
25
26 /// <summary>
27 /// 和@debuger功能致DebuggerAttribute实现
28 /// </summary>
29 public DebuggerAttribute : Attribute, IDecoratorFilter {
30 public Name { get; private ; }
31
32 public DebuggerAttribute( name) {
33 Name = name;
34 }
35
36 #region IDecoratorFilter Members
37
38 public Func<object, object, object> Execute(DecoratorContext context) {
39 (instanse, parameters) => {
40 Console.WriteLine("Debug {0}", Name);
41 Console.WriteLine("Start Debug.");
42 var result = context.Invoker(instanse, parameters);
43 Console.WriteLine("End Debug.");
44 result;
45 };
46 }
47
48 #endregion
49 }
50
51 public TestClass {
52 [Logger("Leven")]
53 [Debugger("test")]
54 public virtual Test {
55 Console.WriteLine("Method TestClass::Test called.");
56 "I am Result.";
57 }
58 }
测试代码很简单:
1 var instanse = TypeFactory.CreateInstanse<TestClass>;
2 Console.WriteLine(instanse.Test);
运行结果如下图所示:
执行得到和Python代码完全致结果至此框架功能实现完毕
接下来用CodeTimer对框架性能做个简单测试,分别对比无代理思路方法,动态代理思路方法,手动继承思路方法执行效率进行测试,测试代码如下:
01 var instanse = TypeFactory.CreateInstanse<TestClass>;
02 TestClass rawInstanse = TestClass;
03 TestClassStatic Instanse = TestClassStatic;
04
05 //first call
06 rawInstanse.Test2;
07 instanse.Test2;
08 Instanse.Test2;
09
10 CodeTimer.Initialize;
11 num = 10000;
12 CodeTimer.Time("Raw Type", num, => {
13 rawInstanse.Test2;
14 });
15 CodeTimer.Time("Proxy Type", num, => {
16 instanse.Test2;
17 });
18 CodeTimer.Time("Static Type", num, => {
19 Instanse.Test2;
20 });
看到结果很是让人始料不及,代理思路方法和手写思路方法执行速度致倒是在预料的中,毕竟IL是完全样,但是有Decorator和无Decorator效率差距实在无法让人接受很显然我们代理思路方法实现效率上无法过关因此本框架虽然功能上已经达到要求但是性能上还有很大优化空间
6. 本篇小结
在本篇中我们完成了框架基本设计让框架成功实现了我们功能目标但是通过测试表明框架性能还远不能达到我们要求因此在下篇中(暂定名:优化篇)我们将详细分析框架性能瓶颈并进行优化使得本框架达到简单高效结果
本文举例源代码或素材下载
最新评论