輕量級AOP架構-移植python的裝飾器(Decorator)到C#(編碼篇)

來源:互聯網
上載者:User
一. 前言

    在《輕量級AOP架構-移植python的裝飾器(Decorator)到C#(思考篇)》中,文章分析了Python中Decorator的原理以及C#移植的可行性,在本篇中,文章將繼續探討如何將這個想法實實在在的表現出來,因此本篇的目標是:一個初級但是可用的Decorator實現。

    如果您對本文的基本思路存在疑惑,請先閱讀思考篇。

二. 實現分析

    上篇中,我們考慮實現一個Wrapper類來做到模仿Python的函數替換功能,然而,在實際使用中,如果靠人工書寫,很顯然是一個不切實際的想法,因此,架構的關鍵在於對被裝飾方法的處理,當前,我們一般使用動態代理或者靜態織入的方式進行該操作,然而,無論是哪種方法,關鍵點都在於對現有代碼的“動態修改”(動態代理的修改在於運行時,靜態織入的修改在於編譯時間)。

    在本篇中,我們考慮一個動態代理的實現,具體的運作方式如下:

  1. 運行時採用架構中的工廠組建代理程式對象,即:調用架構中的Factory 方法,傳入欲產生物件類型。因此對象建立方式將發生改變:預設情況下,我們可能採用var testClass = new TestClass();的方式產生對象,在使用代理的情況下,必須強制使用var testClass = xxx.CreateInstanse<TestClass>();的方式產生對象。
  2. 架構工廠類擷取到物件類型之後,檢查對象是否為可繼承對象,如果不是,則無法組建代理程式類,否則,進行下一步。
  3. 調用動態類生產引擎,產生TestClassWrapper類,並從TestClass繼承。
  4. 採用一定的方式,重寫TestClass中欲進行處理的方法,以滿足上一篇中預設的結果
  5. 產生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<object, object[], object> Execute(DecoratorContext context);}其中的DecoratorContext類包含了欲封裝方法的基本屬性,其定義如下:    public sealed class DecoratorContext {        /// <summary>        /// 該方法通用調用委託        /// </summary>        public Func<object, object[], object> Invoker { get; private set; }        /// <summary>        /// this對象        /// </summary>        public object Instanse { get; private set; }        /// <summary>        /// 方法參數列表        /// </summary>        public object[] Parameters { get; private set; }        public DecoratorContext(Func<object, object[], object> 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);        }        /// <summary>        /// 從Type類派生一個新的代理類        /// </summary>        /// <param name="type">基類</param>        /// <returns></returns>        public TypeBuilder CreateTypeBuilder(Type type) {            TypeBuilder typeBuilder = MainModuleBuilder.DefineType(type.Name + "_" + Guid.NewGuid().ToString(),                TypeAttributes.Class | TypeAttributes.Public,                type);            return typeBuilder;        }        /// <summary>        /// 根據給定的方法對象和類型物件建構CodeGenerator        /// </summary>        /// <param name="typeBuilder"></param>        /// <param name="method"></param>        /// <returns></returns>        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<object, object[], object> 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[i].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[i].ParameterType.IsValueType) {                            cg.Box(parameters[i].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<object, object[], object> 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代碼為例,想辦法達到一樣的效果,準備的代碼如下:

    /// <summary>    /// 和@logger功能一致的LoggerAttribute實現    /// </summary>    public class LoggerAttribute : Attribute, IDecoratorFilter {        public string Name { get; private set; }        public LoggerAttribute(string name) {            Name = name;        }        #region IDecoratorFilter Members        public Func<object, object[], object> 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}    /// <summary>    /// 和@debuger功能一致的DebuggerAttribute實現    /// </summary>    public class DebuggerAttribute : Attribute, IDecoratorFilter {        public string Name { get; private set; }        public DebuggerAttribute(string name) {            Name = name;        }        #region IDecoratorFilter Members        public Func<object, object[], object> 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<TestClass>();Console.WriteLine(instanse.Test());

    運行結果如所示:

    執行得到和Python代碼完全一致的結果,至此,架構的功能實現完畢。

    接下來用CodeTimer對架構的效能做個簡單的測試,分別對比無代理方法,動態代理方法,手動繼承方法的執行效率進行測試,測試代碼如下:

            var instanse = TypeFactory.CreateInstanse<TestClass>();            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();            });

    看到結果很是讓人始料不及,代理方法和手寫方法執行速度一致倒是在預料之中,畢竟IL是完全一樣的,但是有Decorator和無Decorator的效率差距實在無法讓人接受,很顯然,我們代理方法的實現效率上無法過關,因此,本架構雖然功能上已經達到要求,但是效能上還有很大的最佳化空間。

六. 本篇小結

    在本篇中,我們完成了架構的基本設計,讓架構成功的實現了我們的功能目標,但是,通過測試表明,架構的效能還遠不能達到我們的要求,因此,在下一篇中(暫訂名:最佳化篇),我們將詳細分析架構的效能瓶頸並進行最佳化,使得本架構達到簡單高效的結果。

    最後提供目前架構的全部源碼下載(引用其他源碼的著作權歸原作者所有)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.