使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题
理想的代碼優化方式
團隊日常協作中,自然而然的會出現很多重復代碼,根據這些代碼的種類,之前可能會以以下方式處理
| 硬編碼 | 多數新手,或逐漸腐壞的項目會這么干,會直接復制之前實現的代碼 | 帶來的問題顯而易見的多,例如架構會逐漸隨時間被侵蝕,例外越來越多 |
| 提取函數 | 提取成為函數,然后復用 | 提取函數,然后復用,會比直接硬編碼好些,但是仍然存在大量因“例外”而導致增加參數、增加函數重載的情況 |
| 模板生成器 | CodeSmith/T4等 | 因為是獨立進程,所以對于讀取用戶代碼或項目,實現難度較高,且需要現有用戶項目先生成成功,再進行生成 ,或者是完全基于新項目 |
| 代碼片段 | VS自帶的代碼片段功能 | 無法對復雜的環境或條件做出響應 |
| AOP框架 | 面向切面編程,可以解決很多于用戶代碼前后增加操作的事情 | 但是大多AOP框架都是基于透明代理形式實現的,對于相互調用較多的代碼,但形成性能壓力,而且因為要符合透明代理的規則,所以要提供相應的子類或接口。 |
基于Rosyln的編譯時插入代碼
但以上這幾種,AOP算是最理想的方式,但是感覺上還可以有更好的解決方案。
直到讀到了這篇文章?Introducing C# Source Generators,文中提供了一種新的解決方案,即通過Roslyn的Source Generator在編譯時直接讀取當前項目中的語法樹,處理并生成的新代碼,然后在編譯時也使用這些新代碼。
那么如果可以讀取現有代碼的語法樹,通過讀取代碼中的標記,那么在代碼生成過程中是否就能直接生成。
實現如下效果:
項目中的源代碼?Program.cs
自動根據?LogAttribute?自動編譯成的代碼?Program.g.cs
internal class Program { [Log] private static int Add( int a, int b ) { Console.WriteLine("Program.Add(int, int) 開始運行."); int result; result = a + b; Console.WriteLine("Program.Add(int, int) 結束運行."); return result; } }當然LogAttribute中需要去實現插入代碼。
然后項目自動使用新生成的Program.g.cs進行編譯。這樣就實現了基于編譯時的AOP。
即實現以下流程
使用Metalama實現以上流程
經過尋找,發現其實已經有框架可以實現我上面說的流程了,也就是在編譯時實現代碼的插入。
https://www.postsharp.net/metalama 。
下面作一個簡單示例
創建一個.NET6.0的控制臺應用,我這里命名為LogDemo,
其中的入口文件Program.cs
在項目中使用Metalama
通過引用包?https://www.nuget.org/packages/Metalama.Framework, 注意Metalama當前是Preview版本,如果通過可視化Nuget管理器引入,需要注意勾選包含預發行版
dotnet add package Metalama.Framework --version 0.5.7-preview編寫一個AOP的Attribute
在項目中引入?Metalama.Framework后無需多余配置或代碼,直接編寫一個AOP的Attribute
using Metalama.Framework.Aspects;namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 在這個方法中使用了下面的Attribute [LogAttribute] private static int Add(int a, int b) { var result = a + b; Console.WriteLine("Add" + result); return result; } } // 這里是增加的 Attribute public class LogAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() { Console.WriteLine(meta.Target.Method.ToDisplayString() + " 開始運行."); var result = meta.Proceed(); Console.WriteLine(meta.Target.Method.ToDisplayString() + " 結束運行."); return result;} } }執行結果如下
生成的程序集進行反編譯,得到的代碼如下:
總結
這樣就完全實現了我之前想要的效果,當然使用Metalama還可以實現很多能極大地提高生產力的功能,它不僅可以對方法進行改寫,也可以對屬性、字段、事件、甚至是類、命名空間進行一些操作 。
引用
Introducing C# Source Generators:https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/
Metalama官網:https://www.postsharp.net/metalama
總結
以上是生活随笔為你收集整理的使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 发出异步的Get请求
- 下一篇: 饿了么超级会员,年卡低至108元!饿了么