posts - 43,  comments - 394,  views - 13万
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

一. 前言

    在《轻量级AOP框架-移植python的装饰器(Decorator)到C#(思考篇)》中,文章分析了Python中Decorator的原理以及C#移植的可行性,在本篇中,文章将继续探讨如何将这个想法实实在在的表现出来,因此本篇的目标是:一个初级但是可用的Decorator实现。

    如果您对本文的基本思路存在疑惑,请先阅读思考篇。

二. 实现分析

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

    在本篇中,我们考虑一个动态代理的实现,具体的运作方式如下:

  1. 运行时采用框架中的工厂生成代理对象,即:调用框架中的工厂方法,传入欲生成对象类型。因此对象创建方式将发生改变:默认情况下,我们可能采用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接口定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    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类,该类负责动态程序集,动态类型,动态方法的创建,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    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的核心实现,下面给出该方法的实现,该类的完整实现也请参考源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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),那么自动生成的方法大致会像下面一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
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代码为例,想办法达到一样的效果,准备的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    /// <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.";
        }
}

    测试代码很简单:

1
2
var instanse = TypeFactory.CreateInstanse<TestClass>();
Console.WriteLine(instanse.Test());

    运行结果如下图所示:

001

    执行得到和Python代码完全一致的结果,至此,框架的功能实现完毕。

    接下来用CodeTimer对框架的性能做个简单的测试,分别对比无代理方法,动态代理方法,手动继承方法的执行效率进行测试,测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
});

002

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

六. 本篇小结

    在本篇中,我们完成了框架的基本设计,让框架成功的实现了我们的功能目标,但是,通过测试表明,框架的性能还远不能达到我们的要求,因此,在下一篇中(暂定名:优化篇),我们将详细分析框架的性能瓶颈并进行优化,使得本框架达到简单高效的结果。

    最后提供目前框架的全部源码下载(引用其他源码的版权归原作者所有)

posted on   Leven  阅读(2607)  评论(4编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
CopyRight 2008, Leven's Blog xhtml | css
Leven的个人Blog
点击右上角即可分享
微信分享提示