網站首頁 編程語言 正文
Metalama中的Fabric可以做什么
Metalama是一個基于微軟編譯器Roslyn的元編程的庫,可以解決我在開發中遇到的重復代碼的問題。但是其實Metalama不止可以提供編譯時的代碼轉換,更可以提供自定義代碼分析、與IDE結合的自定義代碼修復與代碼重構功能 等功能。
經過面對文檔的學習,發現Metalama可以做到很多非常神奇的事情。
Fabric
通過修改項目、命名空間、類型來達到一些效果,這引起修改包括:添加Aspect
或添加代碼分析
使用Fabric為指定的方法添加Aspect
前文中我們寫過一個簡單的Aspect
:
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; } }
當我們使用它時,我們要在對應的方法上添加這個Attribute
:
[Log] private static int Add(int a, int b) //... ...
那么當我們有一個Aspect
要在項目中大量使用時,在每個方法上添加這個Aspect
當然是一種方法,但是這種方法有2個缺點:
- 包含大量的重復代碼
[Log]
- 對于原代碼的入侵性太強
此時我們就可以使用Fabric
為所有符合要求的方法添加指定的Aspect
:
internal class Fabric : ProjectFabric { // 這個是重寫項目的Fabric中修改項目的方法 public override void AmendProject(IProjectAmender amender) { // 添加 LogAttribute 到符合規則的方法上 // 為名為 Add 且 private 的方法添加 LogAttribute amender.WithTargetMembers(c => c.Types.SelectMany(t => t.Methods) .Where(t => t.Name == "Add" && t.Accessibility == Metalama.Framework.Code.Accessibility.Private) ).AddAspect(t => new LogAttribute()); } }
?
這樣就可以在不入侵現有代碼的情況下為指定的方法添加Aspect
。
使用Fabric添加代碼分析
上文中我們提到,我們可以通過Aspect
為代碼添加代碼分析,當我們要將一個包含(且僅包含)代碼分析的Aspect
應用于一批代碼時,當然我們可以按本文示例1
中的方法,直接使用Fabric
將包含代碼分析的Aspect
應用于指定代碼。
但還有另外一種方法,我們可以直接在Fabric
中定義應用于指定代碼的代碼分析。
下面示例,我們驗證所有類中的私有字段必須符合?_camelCase
,并且使用一個NamespaceFabric
來實現:
namespace FabricCamelCaseDemo; class Fabric : NamespaceFabric { private static readonly DiagnosticDefinition<string> _warning = new( "DEMO04", Severity.Warning, "'{0}'必須使用駝峰命名法并以'_'開頭"); // 這個是命名空間的Fabric中修改命名空間規則 的方法 public override void AmendNamespace(INamespaceAmender amender) { // 取所有非static 的private的字段,并添加代碼分析 amender.WithTargetMembers(c => c.AllTypes.SelectMany(t=>t.Fields) .Where(t => t.Accessibility == Accessibility.Private && !t.IsStatic ) ) //preview 0.5.8之前為 RegisterFinalValidator .Validate(this.FinalValidator); } private void FinalValidator(in DeclarationValidationContext context) { var fullname = context.Declaration.ToDisplayString(); var fieldName = fullname.Split('.').LastOrDefault(); if (fieldName!=null && (!fieldName.StartsWith("_") || !char.IsLower(fieldName[1]))) { context.Diagnostics.Report(_warning.WithArguments(fieldName)); } } }
當然因為當前使用的是NamespaceFabric
所以該規則只應用于當前命名空間如,我們如果在另外一個命名空間中定義一個違反規則的字段的話,并不會有警告。
namespace FabricCamelCase; internal class OtherNamespace { int count = 0; int _total = 0; public int Add() { count++; _total++; return count + _total; } }
使用TypeFabric為類型動態添加方法
開始前偽造一個需求,假設我有一個類AddUtils
專門處理加法操作,它里面應該有從2個到15個參數的Add方法15個(當然我知道,可以使用params
等方法實現,所以這里是個偽需求)。
最終效果為
public class AddUtils { public int Add2(int x1, int x2) { var result = 0; result += x1; result += x2; return 2; } public int Add3(int x1, int x2, int x3) { var result = 0; result += x1; result += x2; result += x3; return 3; } // 以此類推... 下面省去若干方法 }
那么我們可以用Metalama
如此實現
using System.Reflection.Emit; using Metalama.Framework.Aspects; using Metalama.Framework.Fabrics; public class AddUtils { private class Fabric : TypeFabric { // 實現的方法體 [Template] public int MethodTemplate() { var num = (int) meta.Tags["nums"]!; var result = 0; foreach (var targetParameter in meta.Target.Parameters) { result += targetParameter.Value; } return num; } public override void AmendType(ITypeAmender amender) for (var i = 2; i < 15; i++) // 生成一個方法 var methodBuilder = amender.Advices.IntroduceMethod( amender.Type, nameof(this.MethodTemplate), tags: new TagDictionary { ["nums"] = i }); // 方法名 methodBuilder.Name = "Add" + i; // 添加參數 for (int parameterIndex = 1; parameterIndex <= i; parameterIndex++) { methodBuilder.AddParameter($"x{parameterIndex}", typeof(int)); } } }
引用
本章源代碼:https://github.com/chsword/metalama-demo
Metalama官方文檔:?https://doc.metalama.net/
Metalama Nuget包:?https://www.nuget.org/packages/Metalama.Framework/0.5.11-preview
原文鏈接:https://www.cnblogs.com/chsword/p/metalama_4.html
相關推薦
- 2022-05-08 ASP.NET?MVC視圖尋址_實用技巧
- 2022-04-28 WPF依賴屬性用法詳解_實用技巧
- 2022-09-16 go數據結構和算法BitMap原理及實現示例_Golang
- 2022-11-17 docker容器通信參數使用及link參數介紹_docker
- 2022-01-05 npm ERR! code ENOENT npm ERR! syscall open npm ERR
- 2022-06-08 記一次網站全站http升級為https的過程,websocket : ws升級為wss遇到的問題等
- 2022-03-26 asp.net?core?中優雅的進行響應包裝的實現方法_實用技巧
- 2022-12-01 Golang打印復雜結構體兩種方法詳解_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支