1. 程式人生 > 其它 >SourceGenerator 使用姿勢(1):生成代理類,實現簡單的AOP

SourceGenerator 使用姿勢(1):生成代理類,實現簡單的AOP

SourceGenerator 已經出來很久了,也一直在關注。之前觀摩大佬 xljiulang 的 WebApiClient 使用 SourceGenerator 生成介面代理類,深受啟發,準備拿過來用看看(發出白嫖的聲音),寫個編譯期靜態代理AOP。本篇重點是怎麼獲取元資料,得到想要的資料,生成想要的程式碼(往下拖到第 4 點)。

幾個月前寫了個demo,現在趁著有空重新整理完善了下。.net 6 新增了個 IIncrementalGenerator 進行增量編譯,這個還沒研究,後面再說。

我的思路是繼承,生成一個類去繼承需要攔截的實際類,然後重寫相關的方法,此時插入額外的方法,比如 Before,After 等。這就要求相關方法必須是 可重寫 

的, virtualoverride。好了,開幹。

1、定義Aop屬性,打個標籤,SourceGenerator 根據這個標籤查詢相關的 class 或 interface

 1     /// <summary>
 2     /// Aop 攔截器
 3     /// </summary>
 4     public interface IAopInterceptor
 5     {
 6         /// <summary>
 7         /// 執行前操作,同步方法呼叫
 8         /// </summary>
 9         /// <param name="context"></param>
10 /// <returns></returns> 11 AopContext Before(AopContext context); 12 /// <summary> 13 /// 執行前操作,非同步方法呼叫 14 /// </summary> 15 /// <param name="context"></param> 16 /// <returns></returns> 17 ValueTask<AopContext> BeforeAsync(AopContext context);
18 /// <summary> 19 /// 執行後操作,同步方法呼叫 20 /// </summary> 21 /// <param name="context"></param> 22 /// <returns></returns> 23 AopContext After(AopContext context); 24 /// <summary> 25 /// 執行後操作,非同步方法呼叫 26 /// </summary> 27 /// <param name="context"></param> 28 /// <returns></returns> 29 ValueTask<AopContext> AfterAsync(AopContext context); 30 /// <summary> 31 /// 執行方法,同步方法呼叫 32 /// </summary> 33 /// <param name="context"></param> 34 /// <returns></returns> 35 AopContext Next(AopContext context); 36 /// <summary> 37 /// 執行方法,非同步方法呼叫 38 /// </summary> 39 /// <param name="context"></param> 40 /// <returns></returns> 41 ValueTask<AopContext> NextAsync(AopContext context); 42 }

可以不要 IAopInterceptor 這個介面,這裡加了只是為了約束。

 1     public class AopInterceptor : Attribute, IAopInterceptor
 2     {
 3         /// <summary>
 4         /// 是否執行 Before
 5         /// </summary>
 6         public bool HasBefore { get; set; }
 7         /// <summary>
 8         /// 是否執行 After
 9         /// </summary>
10         public bool HasAfter { get; set; }
11         /// <summary>
12         /// 是否執行 Aop 的 Next
13         /// </summary>
14         public bool HasAopNext { get; set; }
15         /// <summary>
16         /// 是否執行實際的方法
17         /// </summary>
18         public bool HasActualNext { get; set; }
19 
20         /// <summary>
21         /// 預設執行所以方法
22         /// </summary>
23         public AopInterceptor()
24         {
25             HasBefore = true;
26             HasAopNext = true;
27             HasActualNext = true;
28             HasAfter = true;
29         }
30 
31         public virtual AopContext Before(AopContext context) => context;
32 
33         public virtual async ValueTask<AopContext> BeforeAsync(AopContext context)
34         {
35             await ValueTask.CompletedTask;
36             return context;
37         }
38 
39         public virtual AopContext After(AopContext context)
40         {
41             return context.Exception != null ? throw context.Exception : context;
42         }
43 
44         public virtual async ValueTask<AopContext> AfterAsync(AopContext context)
45         {
46             if (context.Exception != null)
47                 throw context.Exception;
48 
49             await ValueTask.CompletedTask;
50             return context;
51         }
52 
53         public virtual AopContext Next(AopContext context)
54         {
55             try
56             {
57                 context.Invoke();
58             }
59             catch (Exception e)
60             {
61                 context.Exception = e;
62             }
63             return context;
64         }
65 
66         public virtual async ValueTask<AopContext> NextAsync(AopContext context)
67         {
68             try
69             {
70                 context = await context.InvokeAsync();
71             }
72             catch (Exception e)
73             {
74                 context.Exception = e;
75             }
76 
77             return context;
78         }
79     }
View Code

2、定義上下文,主要包含 是否是非同步,是否有返回值,還有實際方法的委託。決定了呼叫實際方法的時候怎麼呼叫

 1     /// <summary>
 2     /// Aop 上下文
 3     /// </summary>
 4     public struct AopContext
 5     {
 6         /// <summary>
 7         /// 是否是非同步
 8         /// </summary>
 9         public bool IsTask { get; private set; }
10         /// <summary>
11         /// 是否有返回值
12         /// </summary>
13         public bool HasReturnValue { get; private set; }
14         /// <summary>
15         /// 方法輸入引數
16         /// </summary>
17         public Dictionary<string, dynamic> MethodInputParam { get; private set; }
18 
19         /// <summary>
20         /// 實際方法執行結果,可能是 Task
21         /// </summary>
22         public Func<dynamic> ActualMethod { get; set; }
23         /// <summary>
24         /// 返回值,具體的值
25         /// </summary>
26         public dynamic ReturnValue { get; set; }
27         /// <summary>
28         /// 異常資訊
29         /// </summary>
30         public Exception Exception { get; set; }
31         /// <summary>
32         /// IServiceProvider
33         /// </summary>
34         public IServiceProvider ServiceProvider { get; private set; }
35 
36         /// <summary>
37         /// 初始化
38         /// </summary>
39         /// <param name="serviceProvider"></param>
40         /// <param name="methodInputParam"></param>
41         /// <param name="isTask"></param>
42         /// <param name="hasReturnValue"></param>
43         /// <param name="actualMethod"></param>
44         public AopContext(IServiceProvider serviceProvider, Dictionary<string, dynamic> methodInputParam, bool isTask, bool hasReturnValue, Func<dynamic> actualMethod) : this()
45         {
46             ServiceProvider = serviceProvider;
47             MethodInputParam = methodInputParam;
48             IsTask = isTask;
49             HasReturnValue = hasReturnValue;
50             ActualMethod = actualMethod;
51         }
52 
53         /// <summary>
54         /// 執行實際方法 非同步
55         /// </summary>
56         /// <returns></returns>
57         public async ValueTask<AopContext> InvokeAsync()
58         {
59             if (ActualMethod == null)
60                 return this;
61 
62             if (HasReturnValue)
63             {
64                 ReturnValue = await ActualMethod();
65                 return this;
66             }
67 
68             await ActualMethod();
69             return this;
70         }
71 
72         /// <summary>
73         /// 執行實際方法 同步
74         /// </summary>
75         /// <returns></returns>
76         public void Invoke()
77         {
78             if (ActualMethod == null) 
79                 return;
80 
81             //特殊處理 同步且沒有返回值,用 Task.Run 包裝
82             if (!IsTask && !HasReturnValue)
83                 ActualMethod.Invoke().GetAwaiter().GetResult();
84             else
85                 ReturnValue = ActualMethod.Invoke();
86         }
87     }

3、硬編碼實現類

3.1、定義攔截器

 1     /// <summary>
 2     /// 常規服務,執行所有方法
 3     /// </summary>
 4     public class SampleAttribute : AopInterceptor
 5     {
 6         /// <summary>執行前操作,同步方法呼叫</summary>
 7         /// <param name="context"></param>
 8         /// <returns></returns>
 9         public override AopContext Before(AopContext context)
10         {
11             Console.WriteLine("Before...");
12             return base.Before(context);
13         }
14 
15         /// <summary>執行前操作,非同步方法呼叫</summary>
16         /// <param name="context"></param>
17         /// <returns></returns>
18         public override ValueTask<AopContext> BeforeAsync(AopContext context)
19         {
20             Console.WriteLine("BeforeAsync...");
21             return base.BeforeAsync(context);
22         }
23 
24         public override AopContext After(AopContext context)
25         {
26             Console.WriteLine("After...");
27             return context;
28         }
29 
30         /// <summary>執行後操作,非同步方法呼叫</summary>
31         /// <param name="context"></param>
32         /// <returns></returns>
33         public override ValueTask<AopContext> AfterAsync(AopContext context)
34         {
35             Console.WriteLine("AfterAsync...");
36             return base.AfterAsync(context);
37         }
38 
39         /// <summary>執行方法,同步方法呼叫</summary>
40         /// <param name="context"></param>
41         /// <returns></returns>
42         public override AopContext Next(AopContext context)
43         {
44             Console.WriteLine("Next...");
45             return base.Next(context);
46         }
47 
48         /// <summary>執行方法,非同步方法呼叫</summary>
49         /// <param name="context"></param>
50         /// <returns></returns>
51         public override ValueTask<AopContext> NextAsync(AopContext context)
52         {
53             Console.WriteLine("NextAsync...");
54             return base.NextAsync(context);
55         }
56     }
View Code

定義介面

1     public interface ITestService
2     {
3         [Sample]
4         DateTime SampleSync();
5 
6         [Sample]
7         ValueTask<DateTime> SampleAsync();
8     }

3.2、定義實現類

 1     public class TestService : ITestService
 2     {
 3 
 4         public virtual DateTime SampleSync()
 5         {
 6             return DateTime.Now;
 7         }
 8 
 9         public virtual async ValueTask<DateTime> SampleAsync()
10         {
11             await ValueTask.CompletedTask;
12             return DateTime.Now;
13         }
14     }

3.3、定義繼承類,重寫相關方法

 1     public sealed class TestService_Aop : TestService
 2     {
 3         private readonly IServiceProvider _serviceProvider0;
 4         public TestService_Aop(IServiceProvider serviceProvider0)
 5         {
 6             _serviceProvider0 = serviceProvider0;
 7         }
 8 
 9         public override DateTime SampleSync()
10         {
11             var aopContext = new AopContext(_serviceProvider0,
12                 new Dictionary<string, dynamic>() { },
13                 false,
14                 true,
15                 null);
16 
17             var aopInterceptor0 = _serviceProvider0.GetRequiredService<SampleAttribute>();
18             if (aopInterceptor0.HasBefore) aopContext = aopInterceptor0.Before(aopContext);
19             if (aopInterceptor0.HasAopNext)
20             {
21                 if (aopInterceptor0.HasActualNext)
22                 {
23                     aopContext.ActualMethod = () => base.SampleSync();
24                 }
25                 aopContext = aopInterceptor0.Next(aopContext);
26             }
27             else
28             {
29                 if (aopInterceptor0.HasActualNext)
30                 {
31                     aopContext.ReturnValue = base.SampleSync();
32                 }
33             }
34             if (aopInterceptor0.HasAfter) aopContext = aopInterceptor0.After(aopContext);
35 
36             return aopContext.ReturnValue;
37         }
38 
39         public override async ValueTask<DateTime> SampleAsync()
40         {
41             var aopContext = new AopContext(_serviceProvider0,
42                 new Dictionary<string, dynamic>() { },
43                 true,
44                 true,
45                 null);
46 
47             var aopInterceptor0 = _serviceProvider0.GetRequiredService<SampleAttribute>();
48             if (aopInterceptor0.HasBefore) aopContext = await aopInterceptor0.BeforeAsync(aopContext);
49             if (aopInterceptor0.HasAopNext)
50             {
51                 if (aopInterceptor0.HasActualNext)
52                 {
53                     aopContext.ActualMethod = () => base.SampleAsync();
54                 }
55                 aopContext = await aopInterceptor0.NextAsync(aopContext);
56             }
57             else
58             {
59                 if (aopInterceptor0.HasActualNext)
60                 {
61                     aopContext.ReturnValue = await base.SampleAsync();
62                 }
63             }
64             if (aopInterceptor0.HasAfter) aopContext = await aopInterceptor0.AfterAsync(aopContext);
65 
66             return aopContext.ReturnValue;
67         }
68     }

4、開整

4.1、新建專案 Mic.Aop.Generator,TargetFramework 選 netstandard2.0,引入兩個分析器包

<ItemGroup>
        <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
        <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
    </ItemGroup>

4.2、新建類 AopGenerator,繼承 ISourceGenerator 介面,實現 Execute 方法,Execute 的內容是最終的成品。

 1     /// <summary>
 2     /// 程式碼生成器
 3     /// </summary>
 4     [Generator]
 5     public class AopGenerator : ISourceGenerator
 6     {
 7         /// <summary>
 8         /// 初始化
 9         /// </summary>
10         /// <param name="context"></param>
11         public void Initialize(GeneratorInitializationContext context)
12         {
13             //Debugger.Launch();
14 
15             context.RegisterForSyntaxNotifications(() => new AopSyntaxReceiver());
16         }
17 
18         /// <summary>
19         /// 執行
20         /// </summary>
21         /// <param name="context"></param>
22         public void Execute(GeneratorExecutionContext context)
23         {
24             if (context.SyntaxReceiver is AopSyntaxReceiver receiver)
25             {
26                 var aopMateData = receiver
27                     .FindAopInterceptor() // 查詢所有的攔截器 
28                     .GetAopMetaData(context.Compilation); //根據攔截器找到所有的類或方法,獲取元資料,包含所有介面、實現類、所有屬性、所有方法
29 
30                 var builders = aopMateData
31                     .GetAopCodeBuilderMetaData()  //獲取用於構建程式碼的元資料,過濾出需要的資料
32                     .Select(i => new AopCodeBuilder(i))
33                     .Distinct()
34                     .ToList();
35                 //開始生成程式碼
36                 foreach (var builder in builders)
37                 {
38                     context.AddSource(builder.SourceCodeName, builder.ToSourceText());
39                 }
40             }
41         }
42     }

4.3、AopSyntaxReceiver 語法樹處理類,這一步獲取到所有的資料:介面、類、屬性、方法、引數等等等

    /// <summary>
    /// 語法接收器
    /// </summary>
    sealed class AopSyntaxReceiver : ISyntaxReceiver
    {
        private const string GeneratorTagName = "AopInterceptor"; //所有攔截器需要繼承的基類
        private const string IgnoreAttribute = "IgnoreAopAttribute"; //忽略aop
        /// <summary>
        /// 類列表
        /// </summary>
        private readonly List<ClassDeclarationSyntax> _classSyntaxList = new List<ClassDeclarationSyntax>();
        /// <summary>
        /// 介面列表
        /// </summary>
        private readonly List<InterfaceDeclarationSyntax> _interfaceSyntaxList = new List<InterfaceDeclarationSyntax>();
        /// <summary>
        /// 所有的AopInterceptor
        /// </summary>
        public List<string> AopAttributeList = new List<string>();
        /// <summary>
        /// 所有的AopInterceptor
        /// </summary>
        public List<ClassMetaData> AopAttributeClassMetaDataList = new List<ClassMetaData>();

        /// <summary>
        /// 訪問語法樹 
        /// </summary>
        /// <param name="syntaxNode"></param>
        void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
        {
            if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax)
            {
                this._interfaceSyntaxList.Add(interfaceSyntax);
            }

            if (syntaxNode is ClassDeclarationSyntax classSyntax)
            {
                this._classSyntaxList.Add(classSyntax);
            }
        }
        
        //其他程式碼........
    }

4.4、找到所有的攔截器

 1         /// <summary>
 2         /// 找出所有 AopInterceptor 
 3         /// </summary>
 4         /// <returns></returns>
 5         public AopSyntaxReceiver FindAopInterceptor()
 6         {
 7             foreach (var classSyntax in this._classSyntaxList)
 8             {
 9                 var root = classSyntax.SyntaxTree.GetRoot();
10                 var classesWithAttribute = root
11                     .DescendantNodes()
12                     .OfType<ClassDeclarationSyntax>()
13                     .ToList();
14 
15                 if (!classesWithAttribute.Any())
16                     continue;
17 
18                 foreach (var classDeclarationSyntax in classesWithAttribute)
19                 {
20                     if (classDeclarationSyntax.BaseList == null)
21                         continue;
22 
23                     foreach (BaseTypeSyntax baseTypeSyntax in classDeclarationSyntax.BaseList.Types)
24                     {
25                         if (baseTypeSyntax.ToString().Trim() == GeneratorTagName)
26                         {
27                             AopAttributeList.Add(classDeclarationSyntax.Identifier.Text);
28 
29                             var meta = GetClassMetaData(classSyntax);
30                             if (meta != null && AopAttributeClassMetaDataList.All(d => d.Name != meta.Name))
31                                 AopAttributeClassMetaDataList.Add(meta);
32                         }
33                     }
34                 }
35             }
36 
37             AopAttributeList = AopAttributeList.Distinct().ToList();
38 
39             return this;
40         }

4.5、找到所有介面和打了標記的class

 1         /// <summary>
 2         /// 獲取所有打了標記的介面和類
 3         /// </summary>
 4         /// <param name="compilation"></param>
 5         /// <returns></returns>
 6         public AopMetaData GetAopMetaData(Compilation compilation)
 7         {
 8             var result = new AopMetaData(AopAttributeList, IgnoreAttribute, new List<InterfaceMetaData>(), new List<ClassMetaData>());
 9 
10             if (!AopAttributeList.Any())
11                 return result;
12 
13             //處理介面
14             foreach (var classSyntax in this._interfaceSyntaxList)
15             {
16                 var root = classSyntax.SyntaxTree.GetRoot();
17                 var interfaceWithAttribute = root
18                     .DescendantNodes()
19                     .OfType<InterfaceDeclarationSyntax>()
20                     .ToList();
21 
22                 if (!interfaceWithAttribute.Any())
23                     continue;
24 
25                 //處理介面
26                 foreach (var interfaceDeclaration in interfaceWithAttribute)
27                 {
28                     var namespaceName = interfaceDeclaration.FindParent<NamespaceDeclarationSyntax>().Name.ToString();
29                     var className = interfaceDeclaration.Identifier.Text;
30                     var properties = interfaceDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().ToList();
31                     var methodSyntaxs = interfaceDeclaration.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
32                     
33                     //屬性集合
34                     var props = properties.Select(d => new PropertyMetaData(d.Identifier.Text, d.GetAttributeMetaData())).ToList();
35                     //方法集合
36                     var methods = methodSyntaxs.Select(GetMethodMetaData).ToList();
37 
38                     var interfaceMetaData = new InterfaceMetaData(namespaceName, className, interfaceDeclaration.GetAttributeMetaData(), props, methods);
39                     if (interfaceMetaData.MethodMetaData.Any() && !result.InterfaceMetaDataList.Exists(d => d.Equals(interfaceMetaData)))
40                         result.InterfaceMetaDataList.Add(interfaceMetaData);
41                 }
42             }
43 
44             //處理類
45             foreach (var classSyntax in this._classSyntaxList)
46             {
47                 var root = classSyntax.SyntaxTree.GetRoot();
48                 var classesWithAttribute = root
49                     .DescendantNodes()
50                     .OfType<ClassDeclarationSyntax>()
51                     .ToList();
52 
53                 if (!classesWithAttribute.Any())
54                     continue;
55 
56                 foreach (var classDeclaration in classesWithAttribute)
57                 {
58                     var classMetaData = GetClassMetaData(classDeclaration);
59                     if (classMetaData == null)
60                         continue;
61 
62                     if (AopAttributeList.Contains(classMetaData.Name))
63                         continue;
64 
65                     if (classMetaData.MethodMetaData.Any() && !result.ClassMetaDataList.Exists(d => d.Equals(classMetaData)))
66                         result.ClassMetaDataList.Add(classMetaData);
67                 }
68             }
69 
70             result.AopAttributeClassMetaDataList = AopAttributeClassMetaDataList;
71 
72             return result;
73         }

4.6、獲取 class 的資訊:屬性、方法集合、繼承的介面、using引用、建構函式

 1         private ClassMetaData? GetClassMetaData(ClassDeclarationSyntax classDeclaration)
 2         {
 3             var namespaceName = classDeclaration.FindParent<NamespaceDeclarationSyntax>().Name.ToString();
 4             var className = classDeclaration.Identifier.Text;
 5             var properties = classDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().ToList();
 6             var methodSyntaxs = classDeclaration.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
 7 
 8             //屬性集合
 9             var props = properties.Select(d => new PropertyMetaData(d.Identifier.Text, d.GetAttributeMetaData())).ToList();
10             //方法集合
11             var methods = methodSyntaxs.Select(GetMethodMetaData).ToList();
12             //實現的介面集合
13             var interfaces = classDeclaration.BaseList?.ToString().Split(':').Last().Trim().Split(',').Where(d => d.Split('.').Last().StartsWith("I")).ToList() ?? new List<string>();
14             //using 引用
15             var usingDirectiveSyntax = classDeclaration.Parent?.Parent == null ? new SyntaxList<UsingDirectiveSyntax>() : ((CompilationUnitSyntax)classDeclaration.Parent.Parent).Usings;
16             var usings = usingDirectiveSyntax.Select(d => d.ToString()).ToList();
17 
18             //建構函式
19             var constructorDictionary = new List<KeyValueModel>();
20             foreach (var memberDeclarationSyntax in classDeclaration.Members)
21             {
22                 if (memberDeclarationSyntax.Kind().ToString() == "ConstructorDeclaration")
23                 {
24                     //constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>().ToDictionary(d => d.GetFirstToken().Text, d => d.GetLastToken().Text);
25                     constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>().Select(d => new KeyValueModel(d.Type?.ToString(), d.Identifier.Text)).ToList();
26                     break;
27                 }
28             }
29 
30             return new ClassMetaData(namespaceName, className, classDeclaration.GetAttributeMetaData(), props, methods, interfaces, constructorDictionary, usings);
31         }

4.7、獲取 method 的資訊:方法名稱、是否非同步、是否有返回值、是否可重寫、引數資訊、Aop 標記集合(可能有多個)

 1         private MethodMetaData GetMethodMetaData(MethodDeclarationSyntax methodDeclarationSyntax)
 2         {
 3             var param = new List<KeyValueModel>();
 4             var properties = methodDeclarationSyntax.DescendantNodes().OfType<ParameterListSyntax>().FirstOrDefault()?.DescendantNodes().OfType<ParameterSyntax>().ToList() ?? new List<ParameterSyntax>();
 5             foreach (var parameterSyntax in properties)
 6             {
 7                 var type = parameterSyntax?.Type?.ToString();
 8                 var name = parameterSyntax?.Identifier.Text;
 9                 if (type != null && name != null)
10                     param.Add(new KeyValueModel(type, name));
11             }
12 
13             var returnValue = methodDeclarationSyntax.ReturnType.ToString();
14 
15             return new MethodMetaData(methodDeclarationSyntax.Identifier.Text,
16                 methodDeclarationSyntax.GetAttributeMetaData(), returnValue, param, methodDeclarationSyntax.Modifiers.ToString());
17         }

4.8、一頓操作猛如虎,現在我們獲取到了所有的資訊,可以開幹了。這一步處理元資料,過濾出需要生成代理類的資訊。

約定一些規則:

就近原則類方法上的標籤 > 類上的標籤 > 介面方法上的標籤 > 介面上的標籤,即離實際的方法越近,優先順序越高。

忽略Aop:打上 [IgnoreAop] 標籤

管道模式:如果一個方法打上多個Attribute,則按照管道的原則,先進後出,注意,只有最接近方法的 Attribute 才能呼叫 Next 方法。如果有 三個 Attribute,分別是 attribute1、attribute2、attribute3,則執行順序是 attribute1.Before => attribute2.Before => attribute3.Before => attribute3.Next => attribute3.After => attribute2.After => attribute1.After

按照這個約定,過濾得到需要的資料

        public List<AopCodeBuilderMetaData> GetAopCodeBuilderMetaData()
        {
            //就近原則,方法 > 類 > 介面方法 > 介面

            var list = new List<AopCodeBuilderMetaData>();
            foreach (var classMetaData in ClassMetaDataList.Where(d => !AopAttributeList.Contains(d.Name)))
            {
                ////必須要可重寫方法 放出錯誤
                //if (classMetaData.MethodMetaData.All(d => !d.CanOverride))
                //    continue;

                var methods = new List<MethodMetaData>();
                var classHasIgnore = classMetaData.HasIgnore(IgnoreAttribute);

                //實現的介面
                classMetaData.Usings.Add(classMetaData.NameSpace);
                classMetaData.InterfaceMetaData = InterfaceMetaDataList.Where(d => classMetaData.Interfaces.Contains(d.Key)
                    || classMetaData.Interfaces.SelectMany(t => classMetaData.Usings.Select(u => $"{u.Replace("using ", "").Replace(";", "")}.{t.Split('.').Last()}")).Contains(d.Key)).ToList();
                classMetaData.Usings.Remove(classMetaData.NameSpace);

                //按照就近原則過濾
                //foreach (var methodMetaData in classMetaData.MethodMetaData.Where(d => d.CanOverride))
                foreach (var methodMetaData in classMetaData.MethodMetaData)
                {
                    //忽略
                    if (methodMetaData.AttributeMetaData.HasIgnore(IgnoreAttribute))
                        continue;

                    //類方法標記
                    var methodAttrs = methodMetaData.AttributeMetaData.GetAopAttributes(AopAttributeList);
                    if (methodAttrs.Any())
                    {
                        methodMetaData.AttributeMetaData.Clear();
                        methodMetaData.AttributeMetaData.AddRange(methodAttrs);
                        methods.Add(methodMetaData);
                        continue;
                    }

                    //類標記
                    if (classHasIgnore)
                        continue;

                    var classAttr = classMetaData.AttributeMetaData.GetAopAttribute(AopAttributeList);
                    if (classAttr != null)
                    {
                        methodMetaData.AttributeMetaData.Clear();
                        methodMetaData.AttributeMetaData.Add(classAttr);
                        methods.Add(methodMetaData);
                        continue;
                    }

                    //介面標記
                    if (!classMetaData.Interfaces.Any())
                        continue;

                    //介面方法忽略
                    if (classMetaData.InterfaceMetaData.Any(d => d.MethodMetaData.FirstOrDefault(m => m.Key == methodMetaData.Key)?.AttributeMetaData.HasIgnore(IgnoreAttribute) == true))
                        continue;

                    //介面方法標記
                    var interfaceMethodAttr = classMetaData.InterfaceMetaData.Select(d => d.MethodMetaData.FirstOrDefault(m => m.Key == methodMetaData.Key)?.AttributeMetaData.GetAopAttribute(AopAttributeList))
                        .FirstOrDefault(d => d != null);

                    if (interfaceMethodAttr != null)
                    {
                        methodMetaData.AttributeMetaData.Clear();
                        methodMetaData.AttributeMetaData.Add(interfaceMethodAttr);
                        methods.Add(methodMetaData);
                        continue;
                    }

                    //介面標記
                    var interfaceAttr = classMetaData.InterfaceMetaData.Where(d => d.MethodMetaData.Any(d => d.Key == methodMetaData.Key)).Select(d => d.AttributeMetaData.GetAopAttribute(AopAttributeList))
                        .FirstOrDefault(d => d != null);
                    if (interfaceAttr != null)
                    {
                        methodMetaData.AttributeMetaData.Clear();
                        methodMetaData.AttributeMetaData.Add(interfaceAttr);
                        methods.Add(methodMetaData);
                        continue;
                    }
                }

                if (methods.Any())
                    list.Add(new AopCodeBuilderMetaData(classMetaData.NameSpace, classMetaData.Name, methods, classMetaData.Constructor, classMetaData.Usings, classMetaData.InterfaceMetaData));
            }

            return list;
        }

4.9、生成程式碼,生成 3.3 這樣的程式碼。這一步就是程式碼拼接,StringBuilder 一把梭,需要注意的是處理不同的情況如 同步非同步、有無返回值、方法的過載、攔截器的傳值等。程式碼太原始不宜展示,感興趣的可以去看原始碼。整個過程到此結束。

5、不服跑個分

加上aop標籤之後,整個方法呼叫鏈是 aopbefore => aopnext => 執行實際的方法 => aopafter,一共4層,每多一層,耗時就增加,在我的電腦上跑了一下,每增加一層呼叫,大概增加 20~30ns 的耗時。因此根據實際使用場景,增加了 HasBefore、HasAfter、HasAopNext、HasActualNext 這4個判斷去自定義需要執行的方法。詳情見1。

快取場景:有快取,直接 before 裡獲取快取,直接返回,不需要後續的執行,此時只有before;無快取:可以在 AopNext 中執行 ActualNext,更新快取然後返回,或者 執行  ActualNext ,最後在 After 中更新快取,這裡可以省略一個方法呼叫;

業務日誌:只需要執行 ActualNext,然後在 After 中寫日誌,這場景只有2個方法,節省2個方法,美滋滋。

以直接呼叫同步方法為基準,36ns

直接呼叫同步方法:1

直接呼叫非同步方法:2.08

快取場景同步呼叫:5.47

快取場景非同步呼叫:7.45

4個方法火力全開:同步:3.8

4個方法火力全開:非同步:13.5   

程式碼中使用了.net core 自帶的DI 獲取物件,有 幾十ns 的開銷。

6、結尾

SourceGenerator是個好東西,我司在用的場景還有生成一些額外屬性,比如 給Dto中的 列舉、字典、行政區劃等自動新增 Name 屬性,不用手動一個個去處理,延長鍵盤使用壽命。

在這裡提供了一些思路,你們用來做什麼呢?

本文程式碼傳送門:https://github.com/ad313/mic

另外分享一些SourceGenerator的專案:

https://github.com/amis92/csharp-source-generators

https://github.com/Cysharp/MemoryPack