设为首页 收藏本站
查看: 1635|回复: 0

[经验分享] 轻量级AOP框架-移植python的装饰器(Decorator)到C#(编码篇)

[复制链接]

尚未签到

发表于 2015-4-21 06:28:28 | 显示全部楼层 |阅读模式
一. 前言
  在《轻量级AOP框架-移植python的装饰器(Decorator)到C#(思考篇)》中,文章分析了Python中Decorator的原理以及C#移植的可行性,在本篇中,文章将继续探讨如何将这个想法实实在在的表现出来,因此本篇的目标是:一个初级但是可用的Decorator实现。
  如果您对本文的基本思路存在疑惑,请先阅读思考篇。

二. 实现分析
  上篇中,我们考虑实现一个Wrapper类来做到模仿Python的函数替换功能,然而,在实际使用中,如果靠人工书写,很显然是一个不切实际的想法,因此,框架的关键在于对被装饰方法的处理,当前,我们一般使用动态代理或者静态织入的方式进行该操作,然而,无论是哪种方法,关键点都在于对现有代码的“动态修改”(动态代理的修改在于运行时,静态织入的修改在于编译时)。
  在本篇中,我们考虑一个动态代理的实现,具体的运作方式如下:


  • 运行时采用框架中的工厂生成代理对象,即:调用框架中的工厂方法,传入欲生成对象类型。因此对象创建方式将发生改变:默认情况下,我们可能采用var testClass = new TestClass();的方式生成对象,在使用代理的情况下,必须强制使用var testClass = xxx.CreateInstanse();的方式生成对象。
  • 框架工厂类获取到对象类型之后,检查对象是否为可继承对象,如果不是,则无法生成代理类,否则,进行下一步。
  • 调用动态类生产引擎,生成TestClassWrapper类,并从TestClass继承。
  • 采用一定的方式,重写TestClass中欲进行处理的方法,以满足上一篇中预设的结果
  • 生成TestClassWrapper类实例并返回

三. 编码难点
  在了解了具体的运作方式之后,我们可以分别考虑各个步骤的实现难点,第一和第二都不难,使用基本的反射即可实现,主要的问题在于3-5步,下面我们分别对这几步的实现进行编码难点分析。
  对于第三步的类继承,很显然,这首先要具备一个条件,那就是原始类是可继承的,否则,也无从谈起TestClassWrapper的生成,如果满足条件,那么可以使用反射创建动态类。同时,在c#中,我们需要创建一个动态程序集来容纳这些动态类。
  对于第四步,系统需要重写欲处理的方法,要达到这个目标,我们只能请出我们的终极大神MSIL了,在C#中,可以使用Emit的方式进行IL嵌入编程,虽然麻烦了点,但这总算也能高效的解决问题。
  对于第五步,一般来说,可以调用Activator.CreateInstance方法来创建对象,然而,在本人的另一篇博客《探究.net对象的创建,质疑《再谈Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较》中,我们可以了解到Activator.CreateInstance并不是最高效的做法,因此,框架将考虑使用更高效的方式,如Emit或者ExpressionTree的方式生成委托并构造对象。

四. 具体编码
  在编码前,为了方便快速的构造框架,个人将一些成熟的代码放入框架中,以提高效率,这部分代码主要有:FastReflection,可以通过Emit快速构造方法调用委托,CodeGenerator,Nbear框架中的一个IL编程帮助类,通过它可以让我们简化不少IL操作。
  现在万事俱备,我们开始一步步的创造框架吧,首先定义DecoratorFilter接口,以方便扩展Attribute,同时,为了方便,我们也定义一个DecoratorContexe来取代上篇中的直接传入Wrapper方法的委托对象,DecoratorFilter接口定义如下:

    public interface IDecoratorFilter {
Func Execute(DecoratorContext context);
}
其中的DecoratorContext类包含了欲封装方法的基本属性,其定义如下:
public sealed class DecoratorContext {
///
/// 该方法通用调用委托
///
public Func Invoker { get; private set; }
///
/// this对象
///
public object Instanse { get; private set; }
///
/// 方法参数列表
///
public object[] Parameters { get; private set; }
public DecoratorContext(Func invoker, object instanse, object[] parameters) {
Invoker = invoker;
Instanse = instanse;
Parameters = parameters;
}
}

  然后需要完成的是DynamicTypeBuilder类,该类负责动态程序集,动态类型,动态方法的创建,具体实现如下:

    internal class DynamicTypeBuilder {
//为动态程序集生成名称
private readonly string assemblyName = Guid.NewGuid().ToString();
//程序集主模块
private readonly string mainModuleName = "Main";
public AssemblyBuilder AssemblyBuilder { get; private set; }
public ModuleBuilder MainModuleBuilder { get; private set; }
public DynamicTypeBuilder() {
AssemblyName an = new AssemblyName(assemblyName);
//构造一个可运行的动态程序集对象
AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
//为该程序集构造主模块
MainModuleBuilder = AssemblyBuilder.DefineDynamicModule(mainModuleName);
}
///
/// 从Type类派生一个新的代理类
///
/// 基类
///
public TypeBuilder CreateTypeBuilder(Type type) {
TypeBuilder typeBuilder = MainModuleBuilder.DefineType(type.Name + "_" + Guid.NewGuid().ToString(),
TypeAttributes.Class | TypeAttributes.Public,
type);
return typeBuilder;
}
///
/// 根据给定的方法对象和类型对象构造CodeGenerator
///
///
///
///
public CodeGenerator CreateMethodCodeGenerator(TypeBuilder typeBuilder, MethodInfo method) {
CodeGenerator cg = new CodeGenerator(typeBuilder,
method.Name,
MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.HideBySig,
CallingConventions.HasThis,
method.ReturnType,
method.GetParameters().Select(e => e.ParameterType).ToArray());
typeBuilder.DefineMethodOverride(cg.CurrentMethod, method);
return cg;
}
}

  为了提高框架的运行效率,系统也构造一个简单的缓存系统,将缓存一些元数据,详细情况可以参考完整源文件。
  最后考虑最麻烦的TypeFactory类,该类中,最重要的方法就是CreateType,它可以使说框架中最核心的部分了,通过该该方法,我们创建了一个新的Class,该Class中完成了Decorator的核心实现,下面给出该方法的实现,该类的完整实现也请参考源码。

        public static Type CreateType(Type rawType) {
var typeBuilder = builder.CreateTypeBuilder(rawType);
MetaCacheItem metaCacheItem = MetaCache.Get(rawType);
MethodInfo getTypeMethodInfo = rawType.GetMethod("GetType");
foreach (var item in metaCacheItem.Methods) {
if (item.Method.IsVirtual && item.Filters != null && item.Filters.Length > 0) {
var cg = builder.CreateMethodCodeGenerator(typeBuilder, item.Method);
var parameters = item.Method.GetParameters();
var cmpLabel = cg.DefineLabel();
var loopLabel = cg.DefineLabel();
//Type type;
var typeLocal = cg.DeclareLocal(typeType);
//MethodInfo thisMethodInfo;
var thisMethodInfoLocal = cg.DeclareLocal(methodInfoType);
//Type[] typeParameters;
var typeParametersLocal = cg.DeclareLocal(typeArrayType);
//MetaCacheItem metaCacheItem;
var metaCacheItemLocal = cg.DeclareLocal(metaCacheItemType);
//MetaMethodInfo metaMethodInfo;
var metaMethodInfoLocal = cg.DeclareLocal(metaMethodInfoType);
//Func fun;
var funLocal = cg.DeclareLocal(genericInvokerType);
//object[] parameters;
var parametersLocal = cg.DeclareLocal(objectArrayType);
//IDecoratorFilter[] filters;
var filtersLocal = cg.DeclareLocal(iDecoratorFilterArrayType);
//IDecoratorFilter item;
var itemLocal = cg.DeclareLocal(iDecoratorFilterType);
//int num;
var numLocal = cg.DeclareLocal(int32Type);
//type = this.GetType();
//cg.Ldarg(0);
//cg.Call(getTypeMethodInfo);
cg.Ldtoken(rawType);
cg.Call(getTypeFromHandleMethodInfo);
cg.Stloc(typeLocal);
//typeParameters = new Type[] { xxx };
cg.NewArray(typeType, parameters.Length);
cg.Stloc(typeParametersLocal);
for (int i = 0; i < parameters.Length; i++) {
cg.Ldloc(typeParametersLocal);
cg.Ldc(i);
cg.Ldtoken(parameters.ParameterType);
cg.Call(getTypeFromHandleMethodInfo);
//cg.Ldarg(i + 1);
//cg.Call(getTypeMethodInfo);
cg.Stelem(typeType);
}
//thisMethodInfo = type.GetMethod("xxx", typeParameters);
cg.Ldloc(typeLocal);
cg.Ldstr(item.Method.Name);
cg.Ldloc(typeParametersLocal);
cg.Call(getMethodMethodInfo);
cg.Stloc(thisMethodInfoLocal);
//metaCacheItem = MetaCache.Get(type);
cg.Ldloc(typeLocal);
cg.Call(metaCacheGetMethodInfo);
cg.Stloc(metaCacheItemLocal);
//metaMethodInfo = TypeFactory.FindMetaMethodInfo(metaCacheItem.Methods, thisMethod);
cg.Ldloc(metaCacheItemLocal);
cg.Call(metaCacheItemMethodsGetMethodInfo);
cg.Ldloc(thisMethodInfoLocal);
cg.Call(findMetaMethodInfoMethodInfo);
cg.Stloc(metaMethodInfoLocal);
//fun = TypeFactory.CreateGenericInvoker(metaMethodInfo.Method);
cg.Ldloc(metaMethodInfoLocal);
cg.Call(metaMethodInfoMethodGetMethodInfo);
cg.Call(createGenericInvokerMethodInfo);
cg.Stloc(funLocal);
//parameters = new object[] { xxx };
cg.NewArray(objectType, parameters.Length);
cg.Stloc(parametersLocal);
for (int i = 0; i < parameters.Length; i++) {
cg.Ldloc(parametersLocal);
cg.Ldc(i);
cg.Ldarg(i + 1);
if (parameters.ParameterType.IsValueType) {
cg.Box(parameters.ParameterType);
}
cg.Stelem(typeType);
}
cg.Ldloc(metaMethodInfoLocal);
cg.Call(metaMethodInfoFiltersGetMethodInfo);
cg.Stloc(filtersLocal);
//开始循环
cg.Ldc(0);
cg.Stloc(numLocal);
cg.Br(cmpLabel);
cg.MarkLabel(loopLabel);
cg.Ldloc(filtersLocal);
cg.Ldloc(numLocal);
cg.Ldelem(iDecoratorFilterType);
cg.Stloc(itemLocal);
//loop
//item.Execute(new DecoratorContext(fun, this, parameters));
cg.Ldloc(itemLocal);
cg.Ldloc(funLocal);
cg.Ldarg(0);
cg.Ldloc(parametersLocal);
cg.New(decoratorContextConstructorInfo);
cg.Call(iDecoratorFilterExecuteMethodInfo);
cg.Stloc(funLocal);
//endloop
cg.Ldloc(numLocal);
cg.Ldc(1);
cg.Add();
cg.Stloc(numLocal);
cg.MarkLabel(cmpLabel);
cg.Ldloc(numLocal);
cg.Ldloc(filtersLocal);
cg.Ldlen();
cg.Blt(loopLabel);
//return (string)fun(this, parameters)
cg.Ldloc(funLocal);
cg.Ldarg(0);
cg.Ldloc(parametersLocal);
cg.Call(genericInvokeInvokerMethodInfo);
if (item.Method.ReturnType != voidType && item.Method.ReturnType != objectType) {
cg.ConvertValue(objectType, item.Method.ReturnType);
} else if (item.Method.ReturnType == voidType) {
cg.Pop();
}
cg.Ret();
}
}
return typeBuilder.CreateType();
}

  将这部分IL翻译一下,如果我们定义了一个方法public virtual string Test(string p),那么自动生成的方法大致会像下面一样。

        public override string Test(string p) {
Type type = typeof(TestClass);
MethodInfo thisMethod = type.GetMethod("Test", new Type[] { typeof(string) });
MetaCacheItem metaCacheItem = MetaCache.Get(type);
MetaMethodInfo thisMetaMethod = metaCacheItem.Methods.FirstOrDefault(e => e.Method == thisMethod);
Func fun = TypeFactory.CreateGenericInvoker(thisMetaMethod.Method);
object[] parameters = new object[] { p };
foreach (var item in thisMetaMethod.Filters) {
DecoratorContext context = new DecoratorContext(fun, this, parameters);
fun = item.Execute(context);
}
return (string)fun(this, parameters);
}

五. 框架测试
  首先测试功能,我们仍然以上篇中第一个Python代码为例,想办法达到一样的效果,准备的代码如下:

    ///
/// 和@logger功能一致的LoggerAttribute实现
///
public class LoggerAttribute : Attribute, IDecoratorFilter {
public string Name { get; private set; }
public LoggerAttribute(string name) {
Name = name;
}
#region IDecoratorFilter Members
public Func Execute(DecoratorContext context) {
return (instanse, parameters) => {
Console.WriteLine("User is {0}.", Name);
Console.WriteLine("Start Logging.");
var result = context.Invoker(instanse, parameters);
Console.WriteLine("End Logging.");
return result;
};
}
#endregion
}
///
/// 和@debuger功能一致的DebuggerAttribute实现
///
public class DebuggerAttribute : Attribute, IDecoratorFilter {
public string Name { get; private set; }
public DebuggerAttribute(string name) {
Name = name;
}
#region IDecoratorFilter Members
public Func Execute(DecoratorContext context) {
return (instanse, parameters) => {
Console.WriteLine("Debug {0}", Name);
Console.WriteLine("Start Debug.");
var result = context.Invoker(instanse, parameters);
Console.WriteLine("End Debug.");
return result;
};
}
#endregion
}
public class TestClass {
[Logger("Leven")]
[Debugger("test")]
public virtual string Test() {
Console.WriteLine("Method TestClass::Test() called.");
return "I am Result.";
}
}

  测试代码很简单:

var instanse = TypeFactory.CreateInstanse();
Console.WriteLine(instanse.Test());

  运行结果如下图所示:
DSC0000.png
  执行得到和Python代码完全一致的结果,至此,框架的功能实现完毕。
  接下来用CodeTimer对框架的性能做个简单的测试,分别对比无代理方法,动态代理方法,手动继承方法的执行效率进行测试,测试代码如下:

            var instanse = TypeFactory.CreateInstanse();
TestClass rawInstanse = new TestClass();
TestClassStatic staticInstanse = new TestClassStatic();
//first call
rawInstanse.Test2();
instanse.Test2();
staticInstanse.Test2();
CodeTimer.Initialize();
int num = 10000;
CodeTimer.Time("Raw Type", num, () => {
rawInstanse.Test2();
});
CodeTimer.Time("Proxy Type", num, () => {
instanse.Test2();
});
CodeTimer.Time("Static Type", num, () => {
staticInstanse.Test2();
});

DSC0001.png
  看到结果很是让人始料不及,代理方法和手写方法执行速度一致倒是在预料之中,毕竟IL是完全一样的,但是有Decorator和无Decorator的效率差距实在无法让人接受,很显然,我们代理方法的实现效率上无法过关,因此,本框架虽然功能上已经达到要求,但是性能上还有很大的优化空间。

六. 本篇小结
  在本篇中,我们完成了框架的基本设计,让框架成功的实现了我们的功能目标,但是,通过测试表明,框架的性能还远不能达到我们的要求,因此,在下一篇中(暂定名:优化篇),我们将详细分析框架的性能瓶颈并进行优化,使得本框架达到简单高效的结果。
  最后提供目前框架的全部源码下载(引用其他源码的版权归原作者所有)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-58964-1-1.html 上篇帖子: Python tips: 超时装饰器, @timeout decorator 下篇帖子: python中文分词器pymmseg的安装实录
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表