跳到主要内容

優勢與劣勢

對 Metalama 的優點、缺點,以及何時是正確(或錯誤)選擇的誠實評估。


優勢

1. 乾淨、可讀的原始碼

最主要的優點。 橫切關注點以 Attributes 宣告,讓商業邏輯保持簡潔:

// Without AOP: 30+ lines of infrastructure code per method
// With AOP: Business logic stands out clearly
[Authorize]
[Log]
[Cache(AbsoluteExpirationSeconds = 300)]
[Retry(MaxAttempts = 3)]
public async Task<Recipe> GetRecipeAsync(int id)
{
return await _repository.GetByIdAsync(id);
}

2. 零執行期開銷

與基於執行期代理的框架不同,Metalama 在編譯期產生基礎架構程式碼。輸出的組件只包含一般的方法呼叫 — 沒有反射、沒有動態分派、沒有代理開銷。

比較

框架機制開銷
Metalama編譯期程式碼產生
Castle DynamicProxy執行期代理建立代理實例化 + 虛擬分派
DispatchProxy執行期代理建立與 Castle 相同
基於反射執行期反射顯著(反射速度慢)

3. 編譯期錯誤偵測

錯誤在編譯期間就被捕捉,而非在執行期:

[Cache]  // ❌ Compile error: "Cannot cache void methods"
public void ProcessOrder(Order order) { }

[NotifyPropertyChanged] // ❌ Compile error: "Class must be partial"
public class ViewModel { }

4. IDE 整合

Metalama 與 Visual Studio 和 Rider 整合:

  • Code lens 顯示套用了哪些 Aspects
  • 快速修正建議 Aspect 套用
  • 警告和錯誤顯示在 IDE 中
  • 產生的程式碼可在 obj/.../metalama/ 中瀏覽

5. 可除錯的產生程式碼

與 IL 改寫(2023 年前的 PostSharp)不同,Metalama 產生C# 原始碼,你可以:

  • 閱讀和理解
  • 設定中斷點
  • 使用除錯器逐步執行
  • 比較不同版本的差異

6. 關注點分離(真正的模組化)

每個橫切關注點都集中在一個地方

  • 日誌邏輯 → LogAttribute.cs
  • 重試邏輯 → RetryAttribute.cs
  • 稽核邏輯 → AuditAttribute.cs

變更日誌格式只需修改一個檔案,而非 200 個方法。

7. 一致性

當日誌功能實作在一個 Aspect 中,每個使用 [Log] 的方法都會有相同的行為。不會有以下風險:

  • 不同方法中不同的日誌格式
  • 遺漏的 try/catch 區塊
  • 不一致的錯誤處理

8. 可組合性

Aspects 可以堆疊而不會衝突:

[Log]           // ← These don't know about each other
[Retry] // ← But they compose naturally
[Cache] // ← Through the meta.Proceed() pipeline
public Result GetData() { }

9. 合約繼承

驗證合約從介面繼承,確保所有實作的一致性,無需重複撰寫。

10. 可測試性

Aspects 可以獨立測試:

  • 快照測試驗證程式碼產生
  • 執行期測試驗證行為
  • 不需要在每個服務中測試基礎架構程式碼

劣勢

1. 學習曲線

Metalama 引入了幾個新概念:

  • T# Template 語言(編譯期 vs. 執行期程式碼)
  • meta.* API
  • 程式碼模型介面(IMethod、IType 等)
  • 編譯期 vs. 執行期的心智模型

緩解措施:本指南的目的就是降低學習曲線。

2. 建置期複雜度

Metalama 在編譯管線中增加了一個步驟:

  • 較慢的建置:編譯時間增加(大型專案通常增加 10-30%)
  • 建置相依性:Metalama NuGet 套件必須可用
  • CI/CD:建置伺服器需要安裝 Metalama
  • 版本敏感性:Metalama 版本必須在各專案間一致

3. 除錯間接性

雖然產生的程式碼可以除錯,但間接性可能令人困惑:

  • 原始碼與實際執行的內容不符
  • 中斷點必須設定在轉換後的程式碼中
  • 堆疊追蹤顯示產生的方法名稱
  • meta.DebugBreak()Debugger.Break() 的混淆

4. 隱藏行為(「黑魔法」)

Aspects 在呼叫端增加了不可見的行為:

service.GetData();  // ← Is this cached? Logged? Retried? You can't tell.

這可能使不熟悉 Aspects 的開發人員更難理解程式碼。

緩解措施:良好的命名慣例([Cache][Retry])和團隊文件。

5. 廠商鎖定

Metalama 是現代 .NET 唯一成熟的編譯期 AOP 框架:

  • 遷移離開需要將所有 Aspects 重寫為手動程式碼
  • PostSharp(前身)已停止新開發
  • 沒有具備類似功能的開源替代方案

緩解措施:Aspects 封裝良好。如果你需要移除 Metalama,可以機械式地將每個 Aspect 的行為內聯到目標方法中。

6. 僅限 C#

Metalama 僅適用於 C#:

  • 不支援 F#
  • 不支援 VB.NET
  • C++ 互通專案不受影響(Aspects 僅套用於 C# 程式碼)

7. Partial 類別要求

TypeAspect(引入成員的)要求目標類別必須是 partial

// Must be 'partial' to receive introduced members
[NotifyPropertyChanged]
public partial class ViewModel { }

這可能感覺具有侵入性,特別是對現有的程式碼庫。

8. Service Locator 模式(GST 特有)

因為 Aspects 是編譯期的產物,無法使用建構函式注入。GST 框架使用 AspectServiceLocator(靜態 Service Locator),它:

  • 在 DI 社群中被視為反模式
  • 建立了對服務初始化的隱藏相依性
  • 需要在啟動時明確呼叫 InitializeAspects()

緩解措施:這是 Metalama 的限制,而非設計選擇。Metalama 團隊正在探索更好的 DI 整合。Metalama.Extensions.DependencyInjection 套件提供了一些替代方案。

9. 編譯期限制

Template 程式碼必須是編譯期安全的:

  • 無法對引入的成員使用 nameof()
  • 無法在編譯期程式碼中參照僅限執行期的 API
  • 對參數的 foreach 會被展開(可能產生大型方法)
  • 某些 C# 模式在 Templates 中無法使用

10. 授權

Metalama 採用商業授權模式:

  • Free:Metalama Free 適用於開源和小型專案
  • Essential:商業專案的付費授權(有部分限制)
  • Ultimate:完整功能集(較高費用)

詳情請查看 metalama.net/pricing


與替代方案的比較

Metalama vs. PostSharp

面向MetalamaPostSharp
世代新一代(原始碼轉換)舊版(IL 改寫)
原始碼可見性產生的 C# 程式碼可閱讀IL 不透明
除錯標準 C# 除錯需要特殊除錯工具
效能相同或更佳良好
生態系成長中成熟但衰退中
遷移提供遷移指南不適用
狀態積極開發中維護模式

Metalama vs. Castle DynamicProxy

面向MetalamaCastle DynamicProxy
織入編譯期執行期(代理)
效能零開銷代理開銷
範圍任何方法/屬性/欄位僅限介面/虛擬方法
設定NuGet + AttributesDI 容器註冊
除錯產生的 C#執行期代理堆疊
錯誤偵測編譯期執行期

Metalama vs. C# Source Generators

面向MetalamaSource Generators
可否修改現有程式碼✅ 可以❌ 不行(只能新增程式碼)
Template 語言T#(高階)Roslyn 語法樹(低階)
基底類別豐富(OverrideMethodAspect 等)無(自行實作)
可組合性內建(Aspect 管線)手動
學習曲線中等高(Roslyn API)
IDE 支援良好良好

Metalama vs. 手動程式碼 / 不使用 AOP

面向Metalama手動程式碼
可讀性乾淨(Attributes)混雜(交錯的關注點)
一致性保證取決於開發人員
維護性改一處即可改 N 處
學習曲線初始投資
建置複雜度額外步驟
除錯間接直接

何時使用 Metalama

適合的使用情境 ✅

情境原因
許多方法具有相同的基礎架構DRY — 定義一次,到處套用
合規要求(FDA、SOX)一致、可稽核、經認證的 Aspect 行為
MVVM 應用程式(WPF、MAUI)消除 INotifyPropertyChanged 樣板程式碼
函式庫/框架開發對使用者強制執行模式
大型團隊不論開發人員技能水準如何都能維持一致性
長期維護的程式碼庫集中化的關注點管理降低維護成本

不適合的使用情境 ❌

情境原因
小型專案(< 10 個類別)開銷不合理
原型 / 用完即棄的程式碼過度工程
單人開發的腳本沒有一致性的好處
效能關鍵的熱點路徑雖然開銷極小,額外產生的程式碼增加複雜度
不熟悉 AOP 的團隊(未經訓練)「黑魔法」程式碼導致困惑
非 C# 專案不支援

摘要

價值主張

Metalama 在以下情況最有價值

  1. ≥ 10 個方法共享相同的橫切關注點
  2. 需要一致性(合規、團隊標準)
  3. 橫切關注點會隨時間變化(集中化修改)
  4. 團隊願意投資學習框架

成本

  1. 團隊的學習曲線
  2. 建置時間增加
  3. 除錯間接性
  4. 廠商相依性

結論

Metalama 以初始學習投資換取長期可維護性。 對於具有大量橫切關注點的程式碼庫(如 GST 框架),這個取捨是非常正面的。

下一篇最佳實務 — 設計原則與常見陷阱。