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