使用基於Roslyn的編譯時AOP框架來解決.NET項目的代碼復用問題

来源:https://www.cnblogs.com/chsword/archive/2022/04/06/dotnet_compile_aop_base_roslyn.html
-Advertisement-
Play Games

介紹如何通過使用基於Roslyn的編譯時AOP框架來解決.NET項目的代碼復用問題。 可以在項目編譯時自動插入指定代碼,從而避免在運行時帶來的性能消耗。 ...


理想的代碼優化方式

團隊日常協作中,自然而然的會出現很多重覆代碼,根據這些代碼的種類,之前可能會以以下方式處理

方式 描述 應用時可能產生的問題
硬編碼 多數新手,或逐漸腐壞的項目會這麼乾,會直接複製之前實現的代碼 帶來的問題顯而易見的多,例如架構會逐漸隨時間被侵蝕,例外越來越多
提取函數 提取成為函數,然後復用 提取函數,然後復用,會比直接硬編碼好些,但是仍然存在大量因“例外”而導致增加參數、增加函數重載的情況
模板生成器 CodeSmith/T4等 因為是獨立進程,所以對於讀取用戶代碼或項目,實現難度較高,且需要現有用戶項目先生成成功,再進行生成 ,或者是完全基於新項目
代碼片段 VS自帶的代碼片段功能 無法對複雜的環境或條件做出響應
AOP框架 面向切麵編程,可以解決很多於用戶代碼前後增加操作的事情 但是大多AOP框架都是基於透明代理形式實現的,對於相互調用較多的代碼,但形成性能壓力,而且因為要符合透明代理的規則,所以要提供相應的子類或介面。

基於Rosyln的編譯時插入代碼

但以上這幾種,AOP算是最理想的方式,但是感覺上還可以有更好的解決方案。

直到讀到了這篇文章 Introducing C# Source Generators,文中提供了一種新的解決方案,即通過RoslynSource Generator在編譯時直接讀取當前項目中的語法樹,處理並生成的新代碼,然後在編譯時也使用這些新代碼。

那麼如果可以讀取現有代碼的語法樹,通過讀取代碼中的標記,那麼在代碼生成過程中是否就能直接生成。
實現如下效果:
項目中的源代碼 Program.cs

internal class Program
{
    [Log]
    private static int Add( int a, int b )
    {
        return a + b;
    }
}

自動根據 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。

即實現以下流程
image

使用Metalama實現以上流程

經過尋找,發現其實已經有框架可以實現我上面說的流程了,也就是在編譯時實現代碼的插入。
https://www.postsharp.net/metalama

下麵作一個簡單示例

  1. 創建一個.NET6.0的控制台應用,我這裡命名為LogDemo,
    其中的入口文件Program.cs
namespace LogDemo {
    public class Program
    {
        public static void Main(string[] args)
        {
            var r = Add(1, 2);
            Console.WriteLine(r);
        }
        // 這裡寫一個簡單的方法,一會對這個方法進行代碼的插入
        private static int Add(int a, int b)
        {
            var result = a + b;
            Console.WriteLine("Add" + result);
            return result;
        }
    }
}
  1. 在項目中使用Metalama

通過引用包 https://www.nuget.org/packages/Metalama.Framework, 註意Metalama當前是Preview版本,如果通過可視化Nuget管理器引入,需要註意勾選包含預發行版

dotnet add package Metalama.Framework --version 0.5.7-preview
  1. 編寫一個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;

        }
    }
}
  1. 執行結果如下
Program.Add(int, int) 開始運行.
Add3
Program.Add(int, int) 結束運行.
3
  1. 生成的程式集進行反編譯,得到的代碼如下:
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)
        {
            Console.WriteLine("Program.Add(int, int) 開始運行.");
            int result_1;
            var result = a + b;
            Console.WriteLine("Add" + result);
            result_1 = result;
            Console.WriteLine("Program.Add(int, int) 結束運行.");
            return result_1;
        }
    }
#pragma warning disable CS0067
    // 這裡是增加的 Attribute
    public class LogAttribute : OverrideMethodAspect
    {
        public override dynamic? OverrideMethod() => 
        throw new System.NotSupportedException("Compile-time-only code cannot be called at run-time.");
    }
#pragma warning restore CS0067
}

總結

這樣就完全實現了我之前想要的效果,當然使用Metalama還可以實現很多能極大地提高生產力的功能,它不僅可以對方法進行改寫,也可以對屬性、欄位、事件、甚至是類、命名空間進行一些操作 。

引用

Introducing C# Source Generators:https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/
Metalama官網:https://www.postsharp.net/metalama

供大家學習參考,轉文章隨意--重典
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近女朋友在玩連連看,玩了一個星期了還沒通關,真的是菜。 我實在是看不過去了,直接用python寫了個腳本代碼,一分鐘一把游戲。 快是快,就是聯網玩容易被罵,嘿嘿~ 直接上代碼 模塊導入 import cv2 import numpy as np import win32api import win ...
  • 前言 作為目前全世界最大的視頻網站,它幾乎全是用Python來寫的該網站當前行業內線上視頻服務提供商,該網站的系統每天要處理上千萬個視頻片段,為全球成千上萬的用戶提供高水平的視頻上傳、分發、展示、瀏覽服務。2015年2月,央視首次把春晚推送到該網站。今天,我們就要用Python來快速批量下載該網站的 ...
  • 前言 最近疫情真的是非常嚴重,據“百度疫情實時大數據報告”2022年3月27日19點實時數據顯示,上海較昨日新增確診51例,新增無癥狀2633例,形勢嚴峻。 不少在上海的朋友們也尤為關註其所在地周邊的疫情確診情況,涌現了一些小程式幫助我們通過地圖查看周邊的疫情情況。 而今天的文章,我就來帶大家學習如 ...
  • 一、非同步導出Excel文件 1、設計思想 用戶無需在當前頁面等待導出結果,點擊導出按鈕後服務端即可返回前端提示用戶導出處理中請到下載中心查看結果。 具體業務文件導出實現由後臺非同步處理導出文件到騰訊COS存儲(有效期七天,到期自動刪除)。 用戶統一在下載中心菜單欄頁面中查看導出任務結果並下載文件。 2 ...
  • 假期結束了,準備好開始學習了嗎?今天給大家帶來一道列表的題目,快來看看你會不會解。前幾天有小伙伴問了一個Python列表的問題,這裡拿出來給大家分享下,一起學習下。 題目如下: Python學習交流Q群:903971231### SUMMER OF '69: Return the sum of th ...
  • 作者:小李子說程式 來源:https://www.toutiao.com/i6878184496945070604 前言 軟體開發springboot項目過程中,不可避免的需要處理各種異常,spring mvc 架構中各層會出現大量的try {...} catch {...} finally {.. ...
  • 前言 在日常生活中,我們的工作有時候需要對數據進行可視化,讓它一圖標之類的呈現出來。圖給人的感覺是最直觀的,並且能夠一眼就看到數據。 今天我們一起瞭解瀑布圖的重要性,以及如何使用不同的繪圖庫(如 Matplotlib、Plotly)繪製瀑布圖。瀑布圖是一種二維圖表,專門用於瞭解隨著時間或多個步驟或變 ...
  • 一、序言 Java多線程編程線程池被廣泛使用,甚至成為了標配。 線程池本質是池化技術的應用,和連接池類似,創建連接與關閉連接屬於耗時操作,創建線程與銷毀線程也屬於重操作,為了提高效率,先提前創建好一批線程,當有需要使用線程時從線程池取出,用完後放回線程池,這樣避免了頻繁創建與銷毀線程。 // 任務 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...