Skip to content

故障排除

源生成器开发中的常见问题和解决方案

📚 本文档内容

本文档提供源生成器开发中常见问题的解决方案,包括:

  • 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+)

这是最简单和推荐的解决方案:

csharp
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 抑制警告

csharp
#pragma warning disable CS0436
namespace MyGenerator
{
    [System.AttributeUsage(System.AttributeTargets.Class)]
    internal sealed class MyAttribute : System.Attribute
    {
    }
}
#pragma warning restore CS0436

调试技巧

1. 查看生成的代码

方法 1: 在项目文件中启用输出

xml
<PropertyGroup>
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

生成的文件将出现在 obj/Generated/ 文件夹中。

方法 2: 在 IDE 中查看

Visual Studio:

  1. 展开项目节点
  2. 展开 "Dependencies" → "Analyzers"
  3. 展开生成器名称
  4. 查看生成的文件

2. 附加调试器

方法 1: 使用 Debugger.Launch()

csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
    #if DEBUG
    if (!System.Diagnostics.Debugger.IsAttached)
    {
        System.Diagnostics.Debugger.Launch();
    }
    #endif
    
    // 生成器逻辑...
}

方法 2: 手动附加

  1. 在生成器代码中设置断点
  2. 编译使用者项目
  3. 在 Visual Studio 中:Debug → Attach to Process
  4. 选择 csc.exeVBCSCompiler.exe
  5. 重新编译触发断点

3. 日志记录

写入文件:

csharp
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: 生成器没有运行

症状: 编译成功,但没有生成代码

可能原因和解决方案:

  1. 生成器引用配置错误

    xml
    <!-- ❌ 错误 -->
    <ProjectReference Include="..\MyGenerator\MyGenerator.csproj" />
    
    <!-- ✅ 正确 -->
    <ProjectReference Include="..\MyGenerator\MyGenerator.csproj"
                      OutputItemType="Analyzer"
                      ReferenceOutputAssembly="false" />
  2. 缺少 [Generator] 特性

    csharp
    // ❌ 错误
    public class MyGenerator : IIncrementalGenerator
    
    // ✅ 正确
    [Generator]
    public class MyGenerator : IIncrementalGenerator
  3. 目标框架不兼容

    • 生成器必须目标 netstandard2.0

错误 2: 生成的代码无法编译

症状: 编译错误,指向生成的代码

可能原因和解决方案:

  1. 命名空间错误

    csharp
    // 确保生成的代码使用正确的命名空间
    sb.AppendLine($"namespace {classInfo.Namespace}");
  2. 缺少 using 语句

    csharp
    // 添加必要的 using
    sb.AppendLine("using System;");
    sb.AppendLine("using System.Collections.Generic;");
  3. 缺少 partial 关键字

    csharp
    // 生成的类必须是 partial
    sb.AppendLine($"partial class {className}");

错误 3: IntelliSense 不识别生成的代码

症状: 代码可以编译,但 IDE 显示错误

解决方案:

  1. 重新构建项目

    • Clean Solution
    • Rebuild Solution
  2. 重启 IDE

    • 关闭并重新打开 Visual Studio
  3. 删除 .vs 文件夹

错误 4: 性能问题

症状: 编译很慢,IDE 响应慢

解决方案:

  1. 使用增量生成器

    • ISourceGenerator 迁移到 IIncrementalGenerator
  2. 优化过滤逻辑

    csharp
    // ❌ 慢:对所有节点使用语义分析
    predicate: static (node, _) => true
    
    // ✅ 快:先用语法过滤
    predicate: static (node, _) => 
        node is ClassDeclarationSyntax c && 
        c.AttributeLists.Count > 0
  3. 使用 ForAttributeWithMetadataName

    • CreateSyntaxProvider 快得多

诊断工具

1. 查看生成器输出

MSBuild 详细日志:

bash
dotnet build -v detailed > build.log

在日志中搜索生成器名称。

2. 性能分析

测量编译时间:

bash
# 清理
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:

  1. 使用 BenchmarkDotNet 进行微基准测试
  2. 在大型项目中测试
  3. 使用 Visual Studio Profiler
  4. 测量增量编译时间

Q8: 生成器可以异步吗?

A: 不可以。生成器的所有方法都是同步的。

Q9: 如何处理不同的 C# 版本?

A:

  • 检查 context.Compilation.LanguageVersion
  • 根据版本生成不同的代码
  • 在文档中说明最低版本要求

Q10: 生成器可以访问项目配置吗?

A: 可以,通过 AnalyzerConfigOptionsProvider:

csharp
var options = context.AnalyzerConfigOptionsProvider
    .GlobalOptions
    .TryGetValue("build_property.MyOption", out var value);

获取帮助

官方资源

社区资源

报告问题


💡 关键要点

  1. CS0436 警告

    • 使用 [Embedded] 特性(.NET 10+)
    • 或使用共享 DLL
  2. 调试技巧

    • 查看生成的代码
    • 使用 Debugger.Launch()
    • 记录日志
  3. 常见错误

    • 检查生成器引用配置
    • 确保有 [Generator] 特性
    • 验证目标框架
  4. 性能问题

    • 使用增量生成器
    • 优化过滤逻辑
    • 使用 ForAttributeWithMetadataName
  5. 获取帮助

    • 查阅官方文档
    • 搜索社区资源
    • 报告问题时提供详细信息

🔗 相关文档


本文档持续更新中。如有问题或建议,欢迎反馈。

基于 MIT 许可发布