故障排除
源生成器开发中的常见问题和解决方案
📚 本文档内容
本文档提供源生成器开发中常见问题的解决方案,包括:
- CS0436 警告解决方案
- 调试技巧
- 常见错误和解决方案
- 诊断工具
- 常见问题 FAQ
📋 文档信息
| 项目 | 信息 |
|---|---|
| 文档标题 | 故障排除 |
| 所属系列 | 学习指南 |
| 难度级别 | ⭐⭐⭐ 中级 |
| 预计阅读时间 | 45 分钟 |
| 前置知识 | 最佳实践 |
| 相关文档 | 常见模式 |
🎯 学习目标
学完本文档后,你将能够:
- ✅ 解决 CS0436 警告
- ✅ 调试源生成器
- ✅ 解决常见错误
- ✅ 使用诊断工具
- ✅ 回答常见问题
📑 快速导航
| 章节 | 内容概要 | 跳转链接 |
|---|---|---|
| CS0436 警告 | 解决类型冲突警告 | 查看 |
| 调试技巧 | 调试生成器的方法 | 查看 |
| 常见错误 | 常见错误和解决方案 | 查看 |
| 诊断工具 | 使用诊断工具 | 查看 |
| FAQ | 常见问题解答 | 查看 |
概览
本文档提供源生成器开发中常见问题的解决方案,帮助你快速解决问题。
CS0436 警告解决方案
问题描述
警告消息:
CS0436: The type 'MyAttribute' in 'GeneratedFile.cs' conflicts with the imported type 'MyAttribute' in 'Assembly, Version=...'发生原因: 当使用 RegisterPostInitializationOutput 生成特性定义时,如果项目使用了 [InternalsVisibleTo] 特性,编译器会看到两个相同的类型定义。
解决方案
方案 1: 使用 [Embedded] 特性 (.NET 10+)
这是最简单和推荐的解决方案:
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(ctx =>
{
// 1. 添加 EmbeddedAttribute 定义
ctx.AddEmbeddedAttributeDefinition();
// 2. 在生成的特性上应用 [Embedded]
ctx.AddSource("MyAttribute.g.cs", @"
namespace MyGenerator
{
[global::System.Runtime.CompilerServices.Embedded]
[System.AttributeUsage(System.AttributeTargets.Class)]
internal sealed class MyAttribute : System.Attribute
{
}
}");
});
}方案 2: 使用共享 DLL
创建一个独立的项目来定义特性。
方案 3: 使用 #pragma 抑制警告
#pragma warning disable CS0436
namespace MyGenerator
{
[System.AttributeUsage(System.AttributeTargets.Class)]
internal sealed class MyAttribute : System.Attribute
{
}
}
#pragma warning restore CS0436调试技巧
1. 查看生成的代码
方法 1: 在项目文件中启用输出
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>生成的文件将出现在 obj/Generated/ 文件夹中。
方法 2: 在 IDE 中查看
Visual Studio:
- 展开项目节点
- 展开 "Dependencies" → "Analyzers"
- 展开生成器名称
- 查看生成的文件
2. 附加调试器
方法 1: 使用 Debugger.Launch()
public void Initialize(IncrementalGeneratorInitializationContext context)
{
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
}
#endif
// 生成器逻辑...
}方法 2: 手动附加
- 在生成器代码中设置断点
- 编译使用者项目
- 在 Visual Studio 中:Debug → Attach to Process
- 选择
csc.exe或VBCSCompiler.exe - 重新编译触发断点
3. 日志记录
写入文件:
private static void Log(string message)
{
#if DEBUG
var logPath = @"C:\Temp\generator-log.txt";
System.IO.File.AppendAllText(logPath, $"{DateTime.Now}: {message}\n");
#endif
}常见错误和解决方案
错误 1: 生成器没有运行
症状: 编译成功,但没有生成代码
可能原因和解决方案:
生成器引用配置错误
xml<!-- ❌ 错误 --> <ProjectReference Include="..\MyGenerator\MyGenerator.csproj" /> <!-- ✅ 正确 --> <ProjectReference Include="..\MyGenerator\MyGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />缺少 [Generator] 特性
csharp// ❌ 错误 public class MyGenerator : IIncrementalGenerator // ✅ 正确 [Generator] public class MyGenerator : IIncrementalGenerator目标框架不兼容
- 生成器必须目标
netstandard2.0
- 生成器必须目标
错误 2: 生成的代码无法编译
症状: 编译错误,指向生成的代码
可能原因和解决方案:
命名空间错误
csharp// 确保生成的代码使用正确的命名空间 sb.AppendLine($"namespace {classInfo.Namespace}");缺少 using 语句
csharp// 添加必要的 using sb.AppendLine("using System;"); sb.AppendLine("using System.Collections.Generic;");缺少 partial 关键字
csharp// 生成的类必须是 partial sb.AppendLine($"partial class {className}");
错误 3: IntelliSense 不识别生成的代码
症状: 代码可以编译,但 IDE 显示错误
解决方案:
重新构建项目
- Clean Solution
- Rebuild Solution
重启 IDE
- 关闭并重新打开 Visual Studio
删除 .vs 文件夹
错误 4: 性能问题
症状: 编译很慢,IDE 响应慢
解决方案:
使用增量生成器
- 从
ISourceGenerator迁移到IIncrementalGenerator
- 从
优化过滤逻辑
csharp// ❌ 慢:对所有节点使用语义分析 predicate: static (node, _) => true // ✅ 快:先用语法过滤 predicate: static (node, _) => node is ClassDeclarationSyntax c && c.AttributeLists.Count > 0使用 ForAttributeWithMetadataName
- 比
CreateSyntaxProvider快得多
- 比
诊断工具
1. 查看生成器输出
MSBuild 详细日志:
dotnet build -v detailed > build.log在日志中搜索生成器名称。
2. 性能分析
测量编译时间:
# 清理
dotnet clean
# 测量编译时间
Measure-Command { dotnet build }常见问题 FAQ
Q1: 生成器可以修改现有代码吗?
A: 不可以。源生成器只能添加新的源文件,不能修改现有代码。
Q2: 生成器可以访问文件系统吗?
A: 技术上可以,但强烈不推荐。生成器应该是确定性的,只依赖于输入的代码。
Q3: 如何在生成器之间共享代码?
A: 创建一个共享的 NuGet 包,包含通用的辅助方法和数据模型。
Q4: 生成器可以引用第三方库吗?
A: 可以,但要小心:
- 使用
PrivateAssets="all"避免传递依赖 - 确保库兼容
netstandard2.0 - 考虑性能影响
Q5: 如何处理多个生成器?
A: 生成器是独立运行的,互不影响。如果需要协调,可以:
- 使用共享的特性定义
- 通过命名约定避免冲突
- 使用不同的命名空间
Q6: 生成器可以生成非 C# 文件吗?
A: 不可以。源生成器只能生成 C# 源文件(.cs)。
Q7: 如何测试生成器的性能?
A:
- 使用 BenchmarkDotNet 进行微基准测试
- 在大型项目中测试
- 使用 Visual Studio Profiler
- 测量增量编译时间
Q8: 生成器可以异步吗?
A: 不可以。生成器的所有方法都是同步的。
Q9: 如何处理不同的 C# 版本?
A:
- 检查
context.Compilation.LanguageVersion - 根据版本生成不同的代码
- 在文档中说明最低版本要求
Q10: 生成器可以访问项目配置吗?
A: 可以,通过 AnalyzerConfigOptionsProvider:
var options = context.AnalyzerConfigOptionsProvider
.GlobalOptions
.TryGetValue("build_property.MyOption", out var value);获取帮助
官方资源
社区资源
报告问题
- Roslyn GitHub Issues
- 提供最小可重现示例
- 包含版本信息(SDK、Roslyn、IDE)
💡 关键要点
CS0436 警告
- 使用 [Embedded] 特性(.NET 10+)
- 或使用共享 DLL
调试技巧
- 查看生成的代码
- 使用 Debugger.Launch()
- 记录日志
常见错误
- 检查生成器引用配置
- 确保有 [Generator] 特性
- 验证目标框架
性能问题
- 使用增量生成器
- 优化过滤逻辑
- 使用 ForAttributeWithMetadataName
获取帮助
- 查阅官方文档
- 搜索社区资源
- 报告问题时提供详细信息
🔗 相关文档
本文档持续更新中。如有问题或建议,欢迎反馈。