.net8:拦截器Interceptors
在C#12中,引入了拦截器,但一直是试验性的功能,所以自己初步看了一下,没有写文章,最近在看AOT时,发现Dapper.AOT已经用上了这个功能,觉得还是整理一下,分享出来吧,如果以后这个功能改变了,或移除了,请无视这篇文章。
下面是微软官方文档的提示:
拦截器是一项试验性功能,在 C# 12 的预览模式下提供。在将来的版本中,该功能可能会发生中断性变更或被删除。因此,不建议将其用于生产或已发布的应用程序。
其实拦截器实现比较简单,就是定义一个扩展方法,替换掉别的方法,别的方法是通过InterceptsLocation来指定的,只要保证扩展方法和原方法签名一至即可。这里要注意一点,就是需要在项目中定义一下InterceptsLocationAttribute,代码如下:
using System.Runtime.CompilerServices; var myclass = new MyClass(); myclass.Print("测试"); myclass.Print("测试"); public class MyClass { public void Print(string s) { Console.WriteLine($"MyClass.Print({s})"); } } namespace Interceptors { public static class MyClassIntercepts { [InterceptsLocation("C:\\MyFile\\Source\\Repos\\Asp.NetCoreExperiment\\Asp.NetCoreExperiment\\Interceptors\\InterceptorsDemo\\Program.cs", 5, 9)] public static void InterceptorPrint(this MyClass myclass, string s) { Console.WriteLine($"ABC.AOT下 MyClass.InterceptorPrint 拦截 MyClass.Print方法,参数是:{s}"); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class InterceptsLocationAttribute(string filePath, int line, int column) : Attribute { } }
如果想用拦截器,需要在项目文件(.csproj)中添加InterceptorsPreviewNamespaces,显示说明拦截器的信息。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Interceptors</InterceptorsPreviewNamespaces> </PropertyGroup> </Project>
本来拦载器相对简单,就是在编译时作替换即可,这种用法自然使我想起了C#源生成器。什么是源生成器?来看一下一个例子。
首先是一个源生成器的项目TypeMessageGenerator,需要修改一下项目类型和引入Nuget包如下:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>12.0</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" /> </ItemGroup> </Project>
代码实现相对简单,本例就是生成项目中的一些类,类成员等信息,放在外部的一个文件中。
using Microsoft.CodeAnalysis; namespace TypeMessageGenerator { [Generator] public class TypeMembersSourceGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) { var mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken); var path = "C:\\MyFile\\abc\\typemembers.txt"; File.WriteAllText(path, ""); File.AppendAllText(path, context.Compilation.Assembly.Name + "\r\n"); File.AppendAllText(path, "========================\r\n"); foreach (var typename in context.Compilation.Assembly.TypeNames) { File.AppendAllText(path, typename + "\r\n"); try { var mytype = context.Compilation.Assembly.GetTypeByMetadataName(typename); if (mytype == null) { mytype = context.Compilation.Assembly.GetTypeByMetadataName($"{context.Compilation.Assembly.Name}.{typename}"); } if (mytype == null) { continue; } foreach (var member in mytype.GetMembers()) { try { File.AppendAllText(path, $"-- {member.Name} {member.Kind} \r\n"); foreach (var location in member.Locations) { try { File.AppendAllText(path, $"---- {location.GetLineSpan().StartLinePosition.Line},{location.GetLineSpan().EndLinePosition.Character} {location.GetLineSpan().Path}\r\n"); } catch (Exception exc) { File.AppendAllText(path, $"1 {exc.Message} \r\n"); } } } catch (Exception exc) { File.AppendAllText(path, $"2 {exc.Message} \r\n"); } } } catch (Exception exc) { File.AppendAllText(path, $"3 {exc.Message} \r\n"); } } } public void Initialize(GeneratorInitializationContext context) { } } }
怎么使用呢?这里定义了一个控制台项目,添加项目引用,选择上面的TypeMessageGenerator项目,并增加 OutputItemType="Analyzer" ReferenceOutputAssembly="false"这两个属性。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\TypeMessageGenerator\TypeMessageGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup> </Project>
在控制台项目中添加一个Person.cs文件,内容如下:
namespace UserDemoTypeMessageGenerator { public class Person { public int ID { get; set; } public int Name { get; set; } public void PrintPerson() { } } }
在Program.cs中不用动,这时只需要生成项目,就会发现在C:\\MyFile\\abc下生成了typemembers.txt,具体内空如下:
UserDemoTypeMessageGenerator ======================== Program -- <Main>$ Method ---- 0,0 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Program.cs -- .ctor Method ---- 0,7 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Program.cs Person -- <ID>k__BackingField Field ---- 4,21 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- ID Property ---- 4,21 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- get_ID Method ---- 4,27 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- set_ID Method ---- 4,32 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- <Name>k__BackingField Field ---- 6,23 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- Name Property ---- 6,23 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- get_Name Method ---- 6,29 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- set_Name Method ---- 6,34 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- PrintPerson Method ---- 8,31 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs -- .ctor Method ---- 2,23 C:\MyFile\Source\Repos\Asp.NetCoreExperiment\Asp.NetCoreExperiment\SourceGenerator\UserDemoTypeMessageGenerator\Person.cs
这时要他细看,生成的这个Txt中----后,两个数字,一个路径,是不是和拦截器一样,这时你有没有一些想法?
文章来源微信公众号
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?