SourceGenerator于2020年4月29日在微软的.net blog首次介绍,大概说的是开发者编能够写分析器,在项目代码编译时,分析器分析项目既有的静态代码,容许添加源代码到GeneratorExecutionContext中,一同与既有的代码参与编译。git
在尚未SourceGenerator的时候,开发者要实现AOP框架时,每每使用如下技术:github
WebApiClient.JIT与WebApiClient.AOT包,分别适用上面的Emit和Cecil,后者难度很是大,且表现得不太稳定。c#
一直比较关心SourceGenerator,如今我以为,SourceGenerator如今已到达能够使用的阶段了。WebApiClientCore以前有个分支作SourceGenerator的实验,但迟迟没有合并到master来。如今它已经合并到master,并以一个Extensions.SourceGenerator扩展包的方式出现,让WebApiClientCore多一种代理类生成的方式选择。这个扩展包编写时很是简单,我已经不想看之前是怎么用Cecil为程序集插入静态IL的代码了。框架
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>8.0</LangVersion> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" /> </ItemGroup> </Project>
class xxxSyntaxReceiver : ISyntaxReceiver { /// <summary> /// xxx感兴趣的接口列表 /// </summary> private readonly List<InterfaceDeclarationSyntax> interfaceSyntaxList = new List<InterfaceDeclarationSyntax>(); /// <summary> /// 访问语法树 /// </summary> /// <param name="syntaxNode"></param> void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode) { if (syntaxNode is InterfaceDeclarationSyntax syntax) { this.interfaceSyntaxList.Add(syntax); } } }
[Generator] public class xxxSourceGenerator : ISourceGenerator { /// <summary> /// 初始化 /// </summary> /// <param name="context"></param> public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new xxxSyntaxReceiver()); } /// <summary> /// 执行 /// </summary> /// <param name="context"></param> public void Execute(GeneratorExecutionContext context) { if (context.SyntaxReceiver is xxxSyntaxReceiver receiver) { // 从receiver获取你感兴趣的语法节点 // 而后拼接成string的代码 // 把代码添加到context context.AddSource("代码1的id","这里是c#代码,会参与编译的"); } } }
<ItemGroup> <ProjectReference Include="..\xxxSourceGenerator\xxxSourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup>
没错,这是最简单的触发调试方式,你在xxxSourceGenerator入口加这么一行代码,被调试的项目只要一编译,vs就弹出且断点到Debugger.Launch()这行,而后就能够一步一步执行调试了。ui
SourceGenerator项目本质上仍是分析器项目,因此能够打包成一个nuget包,别的项目引用这个nuget包以后,就自动以分析器的方式安装到目标项目中,而后激活了你的xxxSourceGenerator。this
param($installPath, $toolsPath, $package, $project) $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve foreach($analyzersPath in $analyzersPaths) { # Install the language agnostic analyzers. if (Test-Path $analyzersPath) { foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) { if($project.Object.AnalyzerReferences) { $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) } } } } # $project.Type gives the language name like (C# or VB.NET) $languageFolder = "" if($project.Type -eq "C#") { $languageFolder = "cs" } if($project.Type -eq "VB.NET") { $languageFolder = "vb" } if($languageFolder -eq "") { return } foreach($analyzersPath in $analyzersPaths) { # Install language specific analyzers. $languageAnalyzersPath = join-path $analyzersPath $languageFolder if (Test-Path $languageAnalyzersPath) { foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) { if($project.Object.AnalyzerReferences) { $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) } } } }
param($installPath, $toolsPath, $package, $project) $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve foreach($analyzersPath in $analyzersPaths) { # Uninstall the language agnostic analyzers. if (Test-Path $analyzersPath) { foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) { if($project.Object.AnalyzerReferences) { $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) } } } } # $project.Type gives the language name like (C# or VB.NET) $languageFolder = "" if($project.Type -eq "C#") { $languageFolder = "cs" } if($project.Type -eq "VB.NET") { $languageFolder = "vb" } if($languageFolder -eq "") { return } foreach($analyzersPath in $analyzersPaths) { # Uninstall language specific analyzers. $languageAnalyzersPath = join-path $analyzersPath $languageFolder if (Test-Path $languageAnalyzersPath) { foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) { if($project.Object.AnalyzerReferences) { try { $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) } catch { } } } } }
本文讲的SourceGenerator和语法分析器,若是你感兴趣但在实验中遇到困难,你能够下载WebApiClient的源代码来直接体验和调试,而后依葫芦画瓢造本身的SourceGenerator。.net