一直有很多轉載dotnet對Interceptor說明文檔的,但鮮有說明Interceptor如何使用的,這里寫一篇簡單示例來展示一下 c# 12 實驗特性Interceptor 是什么? 官方解釋如下(其實簡單說就是語言特性中內置的靜態(tài)編織方式的aop功能,不同于其他il修改代碼的方式,使用上得結
在dotnet社區(qū)中,雖然有很多關于Interceptor的轉載說明文檔,但很少有關于Interceptor如何使用的詳細說明。本文將通過一個簡單示例來展示Interceptor的使用方法。
Interceptor是一種內置的靜態(tài)編織方式的aop功能,與其他il修改代碼的方式不同。它可以在編譯時以聲明方式將對可攔截方法的調用替換為對其自身的調用。通過讓攔截器聲明所攔截調用的源位置,可以進行這種替換。攔截器可以向編譯中(例如在源生成器中)添加新代碼,從而提供更改現(xiàn)有代碼語義的有限能力。
在源生成器中使用攔截器修改現(xiàn)有編譯的代碼,而非向其中添加代碼。源生成器將對可攔截方法的調用替換為對攔截器方法的調用。
如果你有興趣嘗試攔截器,可以閱讀功能規(guī)范來了解詳細信息。如果使用該功能,請確保隨時了解此實驗功能的功能規(guī)范中的任何更改。最終確定功能后將在微軟文檔站點上添加更多指導。
這里我們用一個簡單的 static method 作為我們改寫方法內容的目標。
public static partial class DBExtensions
{
public static string TestInterceptor(object o)
{
return o.GetType().ToString();
}
}
這樣的靜態(tài)方法,我們假設改寫的目標為返回o參數的其中一個string類型的屬性值。
所以應該可以通過如下的 UT 方法:
[Fact]
public void CallNoError()
{
Assert.Equal("sss", DBExtensions.TestInterceptor(new { A = "sss", C= "ddd" }));
}
建立一個 netstandard2.0 的類庫并設置如下:
netstandard2.0
preview
true
false
Generated 目錄生成代碼文件其實是非必須的,但是為了方便大家看到 source generater 生成的代碼文件內容,對于我們初次嘗試source generater很有幫助。
net8.0
enable
enable
false
true
Generated
$(InterceptorsPreviewNamespaces);Test.AOT
[Generator(LanguageNames.CSharp)]
public class InterceptorGenerator : IIncrementalGenerator
{
}
這里的
IIncrementalGenerator
為source generater更強設計的一代接口,有更強的性能和更方便的能力。接著我們來實現(xiàn)接口。
[Generator(LanguageNames.CSharp)]
public class InterceptorGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var nodes = context.SyntaxProvider.CreateSyntaxProvider(FilterFunc, TransformFunc).Where(x => x is not null).Select((x, _) => x!);
var combined = context.CompilationProvider.Combine(nodes.Collect());
context.RegisterImplementationSourceOutput(combined, Generate);
}
}
接著我們來實現(xiàn)
FilterFunc
。
private bool FilterFunc(SyntaxNode node, CancellationToken token)
{
if (node is InvocationExpressionSyntax ie && ie.ChildNodes().FirstOrDefault() is MemberAccessExpressionSyntax ma)
{
return ma.Name.ToString().StartsWith("TestInterceptor");
}
return false;
}
接著我們來實現(xiàn)
TransformFunc
。
private TestData TransformFunc(GeneratorSyntaxContext ctx, CancellationToken token)
{
// 實現(xiàn)代碼
}
接著我們來實現(xiàn)
Generate
。
private void Generate(SourceProductionContext ctx, (Compilation Left, ImmutableArray Right) state)
{
// 實現(xiàn)代碼
}
如果我們編譯程序,就會看見生成了這樣的文件代碼。
// 生成的代碼
如果運行ut,結果也正確,debug逐行調試也可看到斷點能進入我們生成的代碼文件中。
小編推薦閱讀