第3版:在某个时候,这才刚刚开始工作。不知道为什么。也许是VS的错误被修复了?
Edit2:查看解决方案资源管理器中的Analyzers节点,我发现当我第一次打开程序时,源生成器成功运行,然后它停止了,它生成的所有内容都在对我的代码进行几次更改后消失了。
immediately after opening solution:
> Analyzers
>> MySourceGenerators
>>> MySourceGenerators.NotifyPropertyChangesGenerator
>>>> _NotifyChangedClass_Notify.cs
after making any edits
> Analyzers
>> MySourceGenerators
>>> MySourceGenerators.NotifyPropertyChangesGenerator
>>>> This generator is not generating files.
编辑:按照注释的建议调用Debugger.Launch()
后,我可以确认生成器代码正在运行,源文本看起来与预期完全一样。但IDE和编译器仍然会给出错误,就好像结果没有包括在内一样。
我正试图设置一个源代码生成器,使其从本地项目引用运行,但无法使其实际运行。我的NUnit测试正在通过,所以我知道实际的生成逻辑很好,但基本测试项目在Visual Studio中既无法编译,又会报告错误。我使用的是Visual Studio 2022预览版5.0,以防万一。
<--generator.csproj-->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IncludeBuildOutpout>false</IncludeBuildOutpout>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
</ItemGroup>
</Project>
<--testproject.csproj-->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..MySourceGeneratorsMySourceGenerators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
</ItemGroup>
</Project>
//generator.cs
[Generator]
public class NotifyPropertyChangesGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var receiver = (NotifySyntaxReceiver)context.SyntaxReceiver!;
if (receiver.Classes.Count > 0)
{
foreach (var c in receiver.Classes)
{
/* Generate the source */
var source = SyntaxFactory.ParseCompilationUnit(builder.ToString())
.NormalizeWhitespace()
.GetText(Encoding.UTF8, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha256);
context.AddSource($"_{c.ClassDeclaration.Identifier.ValueText}_Notify", source);
}
}
}
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new NotifySyntaxReceiver());
}
}
class NotifySyntaxReceiver : ISyntaxReceiver
{
public List<NotifyClass> Classes { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax cds)
{
/* Identify classes that need generation */
}
}
}
//testproject.cs
internal class NotifyChangedClass : INotifyPropertyChanged
{
string n_Property;
}
不幸的是,即使在当前的VS 2022(版本17.0.5)中,对该功能的支持也有些有限。正如您所注意到的,VS显示生成的代码的正确状态的唯一时刻是在VS重新启动之后(不仅是解决方案加载/卸载,而且是应用程序的完全重新启动)。当生成器完成时,这不是问题,您只想检查生成了什么,但在生成器开发过程中会很痛苦。因此,我在开发过程中采用了这样一种方法:
在给定的生成器的调试/开发过程中,我们不仅可以将生成的文件的输出添加到编译上下文中,还可以将其添加到文件系统的临时目录中,或者仅添加到临时目录中直到我们对结果感到满意。
为了迫使发电机运行,我们需要强制重建";testproject.csproj";项目我会使用";测试项目";项目目录:"dotnet clean; dotnet build
"。
生成的文件将最终出现在输出目录中。例如,我们可以使用VS代码来观看它们。VS Code不会阻止打开的文件,但任何其他具有非阻止读取功能的记事本都足够了。这不是一个理想的解决方案,但目前它消除了生成器开发的主要痛苦:要查看代码生成的实际结果,我们不必重新启动VS。
";generator.csproj";项目:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace Target.Generators
{
[Generator]
public class TargetGenerator : ISourceGenerator
{
private readonly ISourceBuilder _sourceBuilder;
public TargetGenerator()
{
_sourceBuilder = new SourceBuilder();
}
public void Initialize(GeneratorInitializationContext context) =>
_sourceBuilder.Initialize(context);
public void Execute(GeneratorExecutionContext context)
{
// Uncomment these to lines to start debugging the generator in the separate VS instance
//// Debugger.Launch();
//// Debugger.Break();
// comment/uncomment these lines to use ether 'default' or 'debug' source file writer
////var fileWriter = new DefaultSourceFileWriter(context);
var fileWriter = new DebugSourceFileWriter(context, "C:\code-gen");
var fileBuilders = _sourceBuilder.Build(context);
fileWriter.WriteFiles(fileBuilders);
}
}
public interface ISourceBuilder
{
void Initialize(GeneratorInitializationContext context);
IEnumerable<(string Filename, string Source)> Build(GeneratorExecutionContext context);
}
public class SourceBuilder : ISourceBuilder
{
public void Initialize(GeneratorInitializationContext context)
{
}
public IEnumerable<(string Filename, string Source)> Build(GeneratorExecutionContext context)
{
// Here should be an actual source code generator implementation
throw new NotImplementedException();
}
}
public interface ISourceFileWriter
{
void WriteFiles(IEnumerable<(string Filename, string Source)> sourceFiles);
}
public class DefaultSourceFileWriter : ISourceFileWriter
{
private readonly GeneratorExecutionContext _context;
public DefaultSourceFileWriter(GeneratorExecutionContext context)
{
_context = context;
}
public void WriteFiles(IEnumerable<(string Filename, string Source)> sourceFiles)
{
foreach (var sourceFile in sourceFiles)
{
AddFile(sourceFile);
}
}
protected virtual void AddFile((string Filename, string Source) sourceFile)
{
_context.AddSource(
sourceFile.Filename,
SourceText.From(sourceFile.Source, Encoding.UTF8));
}
}
public class DebugSourceFileWriter : DefaultSourceFileWriter
{
private readonly string _outputDirectoryRoot;
public DebugSourceFileWriter(
GeneratorExecutionContext context,
string outputDirectoryRoot)
: base(context)
{
_outputDirectoryRoot = outputDirectoryRoot;
}
protected override void AddFile((string Filename, string Source) sourceFile)
{
bool done = false;
int cnt = 0;
while (!done)
{
try
{
var fullFileName = Path.Combine(_outputDirectoryRoot, sourceFile.Filename);
File.WriteAllText(fullFileName, sourceFile.Source, Encoding.UTF8);
done = true;
}
catch
{
cnt++;
if (cnt > 5)
{
done = true;
}
Thread.Sleep(100);
}
}
}
}
}
源生成器以netstandard2.0
为目标,您的项目以net6.0
为目标。当您通过PackageReference
使用源生成器时,这不是问题。
我认为要使ProjectReference
在这种情况下工作,您需要添加SetTargetFramework
元数据。
<ItemGroup>
<ProjectReference Include="..MySourceGeneratorsMySourceGenerators.csproj"
OutputItemType="Analyzer"
SetTargetFramework="netstandard2.0"
ReferenceOutputAssembly="false"/>
</ItemGroup>
这可能有效,对不起,现在不能尝试。