一:背景 1. 簡介 .NET 高級調試要想玩的好,看懂彙編是基本功,但看懂彙編和能寫點彙編又完全是兩回事,所以有時候看的多,總手癢癢想寫一點,在 Windows 平臺上搭建彙編環境不是那麼容易,大多還是用微軟的 MASM + DosBox 搭一個 8086 的環境,這玩意距今快 50 年了。 在以 ...
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 這個介面,這裡加了只是為了約束。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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、定義攔截器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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" /> <