架构设计最佳实践
本文档介绍源生成器的架构设计最佳实践,包括模块化设计、关注点分离和依赖管理。
📋 文档信息
| 属性 | 值 |
|---|---|
| 难度 | 中级 |
| 阅读时间 | 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.cs2. 分离关注点
✅ 推荐:清晰的模块划分
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/💡 关键要点
- 模块化设计 - 将生成器拆分为独立的模块
- 关注点分离 - 数据收集、代码生成、工具类分离
- 工厂模式 - 使用工厂创建不同类型的生成器
- 依赖注入 - 提高可测试性和灵活性
- 清晰的结构 - 使用清晰的目录结构组织代码
🔗 相关资源
📖 下一步
继续学习性能优化最佳实践 → 性能优化最佳实践
最后更新: 2025-01-21文档版本: 1.0