Skip to content

架构设计最佳实践

本文档介绍源生成器的架构设计最佳实践,包括模块化设计、关注点分离和依赖管理。


📋 文档信息

属性
难度中级
阅读时间15 分钟
前置知识源生成器基础、设计模式

🎯 学习目标

学完本文档后,你将能够:

  • ✅ 设计模块化的生成器架构
  • ✅ 实现关注点分离
  • ✅ 使用工厂模式和依赖注入
  • ✅ 组织大型生成器项目

🏗️ 模块化设计

1. 推荐的项目结构

MyGenerator/
├── Generators/          # 主生成器
│   └── MyGenerator.cs
├── Collectors/          # 数据收集器
│   └── ClassInfoCollector.cs
├── Analyzers/           # 符号分析器
│   └── SymbolAnalyzer.cs
├── CodeGenerators/      # 代码生成器
│   ├── SerializerCodeGenerator.cs
│   └── BuilderCodeGenerator.cs
├── Models/              # 数据模型
│   ├── ClassInfo.cs
│   └── PropertyInfo.cs
├── Templates/           # 代码模板
│   └── CodeTemplates.cs
├── Utilities/           # 工具类
│   └── SymbolExtensions.cs
└── Constants/           # 常量配置
    └── GeneratorConstants.cs

2. 分离关注点

✅ 推荐:清晰的模块划分

csharp
// 模块 1: 数据模型
namespace MyGenerator.Models
{
    /// <summary>
    /// 类信息数据模型
    /// </summary>
    public record ClassInfo(
        string Name,
        string Namespace,
        ImmutableArray<PropertyInfo> Properties,
        ImmutableArray<MethodInfo> Methods);
    
    /// <summary>
    /// 属性信息数据模型
    /// </summary>
    public record PropertyInfo(
        string Name,
        string Type,
        bool IsNullable);
    
    /// <summary>
    /// 方法信息数据模型
    /// </summary>
    public record MethodInfo(
        string Name,
        string ReturnType,
        ImmutableArray<ParameterInfo> Parameters);
    
    /// <summary>
    /// 参数信息数据模型
    /// </summary>
    public record ParameterInfo(
        string Name,
        string Type);
}

// 模块 2: 数据收集器
namespace MyGenerator.Collectors
{
    /// <summary>
    /// 类信息收集器
    /// </summary>
    public static class ClassInfoCollector
    {
        public static ClassInfo Collect(GeneratorAttributeSyntaxContext context)
        {
            var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
            
            return new ClassInfo(
                classSymbol.Name,
                classSymbol.ContainingNamespace.ToDisplayString(),
                CollectProperties(classSymbol),
                CollectMethods(classSymbol));
        }
        
        private static ImmutableArray<PropertyInfo> CollectProperties(
            INamedTypeSymbol classSymbol)
        {
            return classSymbol.GetMembers()
                .OfType<IPropertySymbol>()
                .Where(p => p.DeclaredAccessibility == Accessibility.Public)
                .Select(p => new PropertyInfo(
                    p.Name,
                    p.Type.ToDisplayString(),
                    p.NullableAnnotation == NullableAnnotation.Annotated))
                .ToImmutableArray();
        }
        
        private static ImmutableArray<MethodInfo> CollectMethods(
            INamedTypeSymbol classSymbol)
        {
            return classSymbol.GetMembers()
                .OfType<IMethodSymbol>()
                .Where(m => m.DeclaredAccessibility == Accessibility.Public)
                .Where(m => m.MethodKind == MethodKind.Ordinary)
                .Select(m => new MethodInfo(
                    m.Name,
                    m.ReturnType.ToDisplayString(),
                    m.Parameters.Select(p => new ParameterInfo(
                        p.Name,
                        p.Type.ToDisplayString()))
                        .ToImmutableArray()))
                .ToImmutableArray();
        }
    }
}

// 模块 3: 代码生成器
namespace MyGenerator.Generators
{
    /// <summary>
    /// 代码生成器基类
    /// </summary>
    public abstract class CodeGeneratorBase
    {
        protected StringBuilder Builder { get; } = new();
        
        public abstract string Generate(ClassInfo classInfo);
        
        protected void AppendLine(string line = "")
        {
            Builder.AppendLine(line);
        }
        
        protected void AppendIndented(string line, int indentLevel)
        {
            Builder.Append(new string(' ', indentLevel * 4));
            Builder.AppendLine(line);
        }
    }
    
    /// <summary>
    /// 序列化代码生成器
    /// </summary>
    public class SerializerCodeGenerator : CodeGeneratorBase
    {
        public override string Generate(ClassInfo classInfo)
        {
            AppendLine($"namespace {classInfo.Namespace}");
            AppendLine("{");
            AppendIndented($"public partial class {classInfo.Name}", 1);
            AppendIndented("{", 1);
            
            GenerateToJsonMethod(classInfo);
            GenerateFromJsonMethod(classInfo);
            
            AppendIndented("}", 1);
            AppendLine("}");
            
            return Builder.ToString();
        }
        
        private void GenerateToJsonMethod(ClassInfo classInfo)
        {
            AppendIndented("public string ToJson()", 2);
            AppendIndented("{", 2);
            AppendIndented("var sb = new System.Text.StringBuilder();", 3);
            AppendIndented("sb.Append(\"{\");", 3);
            
            for (int i = 0; i < classInfo.Properties.Length; i++)
            {
                var prop = classInfo.Properties[i];
                if (i > 0) AppendIndented("sb.Append(\",\");", 3);
                AppendIndented($"sb.Append(\"\\\"{prop.Name}\\\":\");", 3);
                AppendIndented($"sb.Append(System.Text.Json.JsonSerializer.Serialize({prop.Name}));", 3);
            }
            
            AppendIndented("sb.Append(\"}\");", 3);
            AppendIndented("return sb.ToString();", 3);
            AppendIndented("}", 2);
        }
        
        private void GenerateFromJsonMethod(ClassInfo classInfo)
        {
            AppendIndented($"public static {classInfo.Name} FromJson(string json)", 2);
            AppendIndented("{", 2);
            AppendIndented($"return System.Text.Json.JsonSerializer.Deserialize<{classInfo.Name}>(json);", 3);
            AppendIndented("}", 2);
        }
    }
}

// 模块 4: 主生成器
namespace MyGenerator
{
    /// <summary>
    /// 主源生成器
    /// </summary>
    [Generator]
    public class MainGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            // 收集类信息
            var classInfos = context.SyntaxProvider
                .ForAttributeWithMetadataName(
                    "SerializableAttribute",
                    predicate: (node, _) => node is ClassDeclarationSyntax,
                    transform: (ctx, _) => ClassInfoCollector.Collect(ctx));
            
            // 生成代码
            context.RegisterSourceOutput(classInfos, (spc, classInfo) =>
            {
                var generator = new SerializerCodeGenerator();
                var code = generator.Generate(classInfo);
                spc.AddSource($"{classInfo.Name}_Serializer.g.cs", code);
            });
        }
    }
}

优势:

  • 清晰的职责划分
  • 易于测试和维护
  • 可复用的组件
  • 易于扩展新功能

🏭 使用工厂模式

工厂模式创建生成器

✅ 推荐:工厂模式

csharp
/// <summary>
/// 代码生成器工厂
/// </summary>
public static class CodeGeneratorFactory
{
    public static ICodeGenerator Create(GeneratorType type)
    {
        return type switch
        {
            GeneratorType.Serializer => new SerializerCodeGenerator(),
            GeneratorType.Builder => new BuilderCodeGenerator(),
            GeneratorType.Mapper => new MapperCodeGenerator(),
            _ => throw new ArgumentException($"Unknown generator type: {type}")
        };
    }
}

/// <summary>
/// 代码生成器接口
/// </summary>
public interface ICodeGenerator
{
    string Generate(ClassInfo classInfo);
}

/// <summary>
/// 生成器类型
/// </summary>
public enum GeneratorType
{
    Serializer,
    Builder,
    Mapper
}

// 使用工厂
[Generator]
public class FlexibleGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var classInfos = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "GenerateAttribute",
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) =>
                {
                    var classInfo = ClassInfoCollector.Collect(ctx);
                    var generatorType = GetGeneratorType(ctx);
                    return (classInfo, generatorType);
                });
        
        context.RegisterSourceOutput(classInfos, (spc, data) =>
        {
            var generator = CodeGeneratorFactory.Create(data.generatorType);
            var code = generator.Generate(data.classInfo);
            spc.AddSource($"{data.classInfo.Name}.g.cs", code);
        });
    }
    
    private GeneratorType GetGeneratorType(GeneratorAttributeSyntaxContext context)
    {
        var attr = context.Attributes.First();
        var typeArg = attr.ConstructorArguments.FirstOrDefault();
        return (GeneratorType)(typeArg.Value ?? GeneratorType.Serializer);
    }
}

💉 依赖注入模式

使用依赖注入提高可测试性

✅ 推荐:依赖注入

csharp
/// <summary>
/// 符号分析器接口
/// </summary>
public interface ISymbolAnalyzer
{
    bool IsValidClass(INamedTypeSymbol symbol);
    ImmutableArray<PropertyInfo> GetProperties(INamedTypeSymbol symbol);
}

/// <summary>
/// 符号分析器实现
/// </summary>
public class SymbolAnalyzer : ISymbolAnalyzer
{
    public bool IsValidClass(INamedTypeSymbol symbol)
    {
        return symbol.IsPartialDefinition() &&
               symbol.DeclaredAccessibility == Accessibility.Public;
    }
    
    public ImmutableArray<PropertyInfo> GetProperties(INamedTypeSymbol symbol)
    {
        return symbol.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => p.DeclaredAccessibility == Accessibility.Public)
            .Select(p => new PropertyInfo(p.Name, p.Type.ToDisplayString()))
            .ToImmutableArray();
    }
}

/// <summary>
/// 可测试的生成器
/// </summary>
[Generator]
public class TestableGenerator : IIncrementalGenerator
{
    private readonly ISymbolAnalyzer _analyzer;
    
    // 用于测试的构造函数
    public TestableGenerator() : this(new SymbolAnalyzer())
    {
    }
    
    // 用于依赖注入的构造函数
    public TestableGenerator(ISymbolAnalyzer analyzer)
    {
        _analyzer = analyzer;
    }
    
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var classInfos = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "GenerateAttribute",
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) =>
                {
                    var symbol = (INamedTypeSymbol)ctx.TargetSymbol;
                    
                    if (!_analyzer.IsValidClass(symbol))
                        return null;
                    
                    var properties = _analyzer.GetProperties(symbol);
                    return new ClassInfo(symbol.Name, properties);
                });
        
        context.RegisterSourceOutput(classInfos.Where(x => x != null), GenerateCode);
    }
    
    private void GenerateCode(SourceProductionContext context, ClassInfo classInfo)
    {
        var code = $"public partial class {classInfo.Name} {{ }}";
        context.AddSource($"{classInfo.Name}.g.cs", code);
    }
}

单元测试示例:

csharp
public class TestableGeneratorTests
{
    [Fact]
    public void Generator_UsesAnalyzer_Correctly()
    {
        // 创建模拟分析器
        var mockAnalyzer = new Mock<ISymbolAnalyzer>();
        mockAnalyzer.Setup(a => a.IsValidClass(It.IsAny<INamedTypeSymbol>()))
            .Returns(true);
        mockAnalyzer.Setup(a => a.GetProperties(It.IsAny<INamedTypeSymbol>()))
            .Returns(ImmutableArray<PropertyInfo>.Empty);
        
        // 使用模拟分析器创建生成器
        var generator = new TestableGenerator(mockAnalyzer.Object);
        
        // 运行生成器并验证
        // ...
        
        // 验证分析器被调用
        mockAnalyzer.Verify(a => a.IsValidClass(It.IsAny<INamedTypeSymbol>()), Times.Once);
    }
}

📦 组织大型项目

推荐的组织方式

MyGenerator/
├── Generators/          # 主生成器
│   ├── MainGenerator.cs
│   └── AnotherGenerator.cs
├── Collectors/          # 数据收集器
│   ├── ClassInfoCollector.cs
│   └── PropertyInfoCollector.cs
├── Analyzers/           # 符号分析器
│   ├── SymbolAnalyzer.cs
│   └── TypeAnalyzer.cs
├── CodeGenerators/      # 代码生成器
│   ├── SerializerCodeGenerator.cs
│   ├── BuilderCodeGenerator.cs
│   └── MapperCodeGenerator.cs
├── Models/              # 数据模型
│   ├── ClassInfo.cs
│   ├── PropertyInfo.cs
│   └── MethodInfo.cs
├── Templates/           # 代码模板
│   ├── SerializerTemplate.cs
│   └── BuilderTemplate.cs
├── Utilities/           # 工具类
│   ├── SymbolExtensions.cs
│   ├── CodeFormatter.cs
│   └── StringHelper.cs
├── Constants/           # 常量配置
│   ├── GeneratorConstants.cs
│   └── DiagnosticDescriptors.cs
└── Tests/               # 测试
    ├── Unit/
    ├── Integration/
    └── Performance/

💡 关键要点

  1. 模块化设计 - 将生成器拆分为独立的模块
  2. 关注点分离 - 数据收集、代码生成、工具类分离
  3. 工厂模式 - 使用工厂创建不同类型的生成器
  4. 依赖注入 - 提高可测试性和灵活性
  5. 清晰的结构 - 使用清晰的目录结构组织代码

🔗 相关资源


📖 下一步

继续学习性能优化最佳实践 → 性能优化最佳实践


最后更新: 2025-01-21文档版本: 1.0

基于 MIT 许可发布