SourceGenerator 已經出來很久了,也一直在關注。之前觀摩大佬 xljiulang 的 WebApiClient 使用 SourceGenerator 生成介面代理類,深受啟發,準備拿過來用看看(發出白嫖的聲音),寫個編譯期靜態代理AOP。本篇重點是怎麼獲取後設資料,得到想要的資料,生成想要的程式碼(往下拖到第 4 點)。
幾個月前寫了個demo,現在趁著有空重新整理完善了下。.net 6 新增了個 IIncrementalGenerator 進行增量編譯,這個還沒研究,後面再說。
我的思路是繼承,生成一個類去繼承需要攔截的實際類,然後重寫相關的方法,此時插入額外的方法,比如 Before,After 等。這就要求相關方法必須是 可重寫 的, virtual 或 override。好了,開幹。
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 }
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 }
定義介面
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