一直有很多转载dotnet对Interceptor说明文档的,但鲜有说明Interceptor如何使用的,这里写一篇简单示例来展示一下 c# 12 实验特性Interceptor 是什么? 官方解释如下(其实简单说就是语言特性中内置的静态编织方式的aop功能,不同于其他il修改代码的方式,使用上得结
在dotnet社区中,虽然有很多关于Interceptor的转载说明文档,但很少有关于Interceptor如何使用的详细说明。本文将通过一个简单示例来展示Interceptor的使用方法。
Interceptor是一种内置的静态编织方式的aop功能,与其他il修改代码的方式不同。它可以在编译时以声明方式将对可拦截方法的调用替换为对其自身的调用。通过让拦截器声明所拦截调用的源位置,可以进行这种替换。拦截器可以向编译中(例如在源生成器中)添加新代码,从而提供更改现有代码语义的有限能力。
在源生成器中使用拦截器修改现有编译的代码,而非向其中添加代码。源生成器将对可拦截方法的调用替换为对拦截器方法的调用。
如果你有兴趣尝试拦截器,可以阅读功能规范来了解详细信息。如果使用该功能,请确保随时了解此实验功能的功能规范中的任何更改。最终确定功能后将在微软文档站点上添加更多指导。
这里我们用一个简单的 static method 作为我们改写方法内容的目标。
public static partial class DBExtensions
{
public static string TestInterceptor(object o)
{
return o.GetType().ToString();
}
}
这样的静态方法,我们假设改写的目标为返回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更强设计的一代接口,有更强的性能和更方便的能力。接着我们来实现接口。
[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);
}
}
接着我们来实现
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;
}
接着我们来实现
TransformFunc
。
private TestData TransformFunc(GeneratorSyntaxContext ctx, CancellationToken token)
{
// 实现代码
}
接着我们来实现
Generate
。
private void Generate(SourceProductionContext ctx, (Compilation Left, ImmutableArray Right) state)
{
// 实现代码
}
如果我们编译程序,就会看见生成了这样的文件代码。
// 生成的代码
如果运行ut,结果也正确,debug逐行调试也可看到断点能进入我们生成的代码文件中。
小编推荐阅读