高级实战场景
本文档介绍源生成器开发中的高级实战场景,包括多阶段代码生成、动态模板系统、条件编译、智能缓存等。
📋 文档信息
- 难度级别: 高级
- 预计阅读时间: 30 分钟
- 前置知识:
- 增量生成器高级用法
- C# 高级特性
- 设计模式
🎯 学习目标
完成本文档后,您将能够:
- ✅ 实现多阶段代码生成
- ✅ 创建动态模板系统
- ✅ 实现条件编译
- ✅ 使用智能缓存策略
- ✅ 实现跨生成器通信
- ✅ 应用真实使用场景
场景 1: 多阶段代码生成
在某些情况下,您可能需要分多个阶段生成代码,每个阶段依赖于前一个阶段的结果。
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 阶段 1: 收集所有类信息
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx))
.Collect();
// 阶段 2: 分析类之间的关系
var relationships = classInfos.Select((classes, _) =>
{
var graph = new Dictionary<string, List<string>>();
foreach (var cls in classes)
{
var dependencies = AnalyzeDependencies(cls, classes);
graph[cls.Name] = dependencies;
}
return graph;
});
// 阶段 3: 按依赖顺序生成代码
var combined = classInfos.Combine(relationships);
context.RegisterSourceOutput(combined, (spc, data) =>
{
var (classes, graph) = data;
var sorted = TopologicalSort(graph);
foreach (var className in sorted)
{
var classInfo = classes.First(c => c.Name == className);
var code = GenerateCode(classInfo, graph);
spc.AddSource($"{className}.g.cs", code);
}
});
}
private List<string> AnalyzeDependencies(
ClassInfo classInfo,
ImmutableArray<ClassInfo> allClasses)
{
var dependencies = new List<string>();
foreach (var property in classInfo.Properties)
{
var propertyType = property.Type;
if (allClasses.Any(c => c.Name == propertyType))
{
dependencies.Add(propertyType);
}
}
return dependencies;
}
private List<string> TopologicalSort(Dictionary<string, List<string>> graph)
{
var result = new List<string>();
var visited = new HashSet<string>();
var visiting = new HashSet<string>();
void Visit(string node)
{
if (visited.Contains(node)) return;
if (visiting.Contains(node))
throw new InvalidOperationException("Circular dependency detected");
visiting.Add(node);
if (graph.ContainsKey(node))
{
foreach (var dependency in graph[node])
{
Visit(dependency);
}
}
visiting.Remove(node);
visited.Add(node);
result.Add(node);
}
foreach (var node in graph.Keys)
{
Visit(node);
}
return result;
}场景 2: 动态模板系统
实现一个灵活的模板系统,支持从外部文件加载模板。
csharp
public class TemplateEngine
{
private readonly Dictionary<string, string> _templates = new();
public void LoadTemplates(IncrementalGeneratorInitializationContext context)
{
var templateFiles = context.AdditionalTextsProvider
.Where(file => file.Path.EndsWith(".template"))
.Select((file, ct) => new
{
Name = Path.GetFileNameWithoutExtension(file.Path),
Content = file.GetText(ct)?.ToString() ?? string.Empty
});
// 缓存模板
foreach (var template in templateFiles)
{
_templates[template.Name] = template.Content;
}
}
public string Render(string templateName, object model)
{
if (!_templates.TryGetValue(templateName, out var template))
{
throw new ArgumentException($"Template '{templateName}' not found");
}
// 简单的模板替换
var result = template;
var properties = model.GetType().GetProperties();
foreach (var prop in properties)
{
var value = prop.GetValue(model)?.ToString() ?? string.Empty;
result = result.Replace($"{{{{{prop.Name}}}}}", value);
}
return result;
}
}
// 使用示例
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var engine = new TemplateEngine();
engine.LoadTemplates(context);
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx));
context.RegisterSourceOutput(classInfos, (spc, classInfo) =>
{
var code = engine.Render("ClassTemplate", new
{
ClassName = classInfo.Name,
Namespace = classInfo.Namespace,
Properties = string.Join("\n", classInfo.Properties.Select(p =>
$" public {p.Type} {p.Name} {{ get; set; }}"))
});
spc.AddSource($"{classInfo.Name}.g.cs", code);
});
}场景 3: 代码生成的条件编译
根据不同的编译条件生成不同的代码。
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 读取编译配置
var buildConfig = context.AnalyzerConfigOptionsProvider
.Select((opts, _) =>
{
opts.GlobalOptions.TryGetValue(
"build_property.Configuration",
out var config);
opts.GlobalOptions.TryGetValue(
"build_property.TargetFramework",
out var framework);
return new BuildConfig
{
Configuration = config ?? "Debug",
TargetFramework = framework ?? "net6.0"
};
});
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx));
var combined = buildConfig.Combine(classInfos);
context.RegisterSourceOutput(combined, (spc, data) =>
{
var (config, classInfo) = data;
var code = GenerateConditionalCode(classInfo, config);
spc.AddSource($"{classInfo.Name}.g.cs", code);
});
}
private string GenerateConditionalCode(ClassInfo classInfo, BuildConfig config)
{
var sb = new StringBuilder();
sb.AppendLine("// <auto-generated />");
sb.AppendLine();
// 根据配置添加不同的代码
if (config.Configuration == "Debug")
{
sb.AppendLine("#if DEBUG");
sb.AppendLine("using System.Diagnostics;");
sb.AppendLine("#endif");
}
sb.AppendLine($"namespace {classInfo.Namespace}");
sb.AppendLine("{");
sb.AppendLine($" public partial class {classInfo.Name}");
sb.AppendLine(" {");
// Debug 模式下添加额外的诊断方法
if (config.Configuration == "Debug")
{
sb.AppendLine("#if DEBUG");
sb.AppendLine(" public void DebugInfo()");
sb.AppendLine(" {");
sb.AppendLine($" Debug.WriteLine(\"Class: {classInfo.Name}\");");
sb.AppendLine(" }");
sb.AppendLine("#endif");
}
// 根据目标框架生成不同的代码
if (config.TargetFramework.StartsWith("net6") ||
config.TargetFramework.StartsWith("net7") ||
config.TargetFramework.StartsWith("net8"))
{
sb.AppendLine(" // .NET 6+ specific code");
sb.AppendLine(" public required string Id { get; init; }");
}
else
{
sb.AppendLine(" // Legacy .NET code");
sb.AppendLine(" public string Id { get; set; }");
}
sb.AppendLine(" }");
sb.AppendLine("}");
return sb.ToString();
}
public record BuildConfig
{
public string Configuration { get; init; }
public string TargetFramework { get; init; }
}场景 4: 增量更新的智能缓存
实现智能缓存策略,只在真正需要时重新生成代码。
csharp
public class SmartCacheGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 收集类信息并计算哈希
var classInfosWithHash = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
var classInfo = GetClassInfo(ctx);
var hash = ComputeHash(classInfo);
return new ClassInfoWithHash(classInfo, hash);
});
// 使用自定义比较器
var cached = classInfosWithHash
.WithComparer(new ClassInfoHashComparer());
context.RegisterSourceOutput(cached, (spc, data) =>
{
// 只有当哈希改变时才会执行
var code = GenerateCode(data.ClassInfo);
spc.AddSource($"{data.ClassInfo.Name}.g.cs", code);
});
}
private string ComputeHash(ClassInfo classInfo)
{
using var sha256 = SHA256.Create();
var content = $"{classInfo.Name}|{classInfo.Namespace}|" +
string.Join("|", classInfo.Properties.Select(p =>
$"{p.Name}:{p.Type}"));
var bytes = Encoding.UTF8.GetBytes(content);
var hashBytes = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hashBytes);
}
}
public record ClassInfoWithHash(ClassInfo ClassInfo, string Hash);
public class ClassInfoHashComparer : IEqualityComparer<ClassInfoWithHash>
{
public bool Equals(ClassInfoWithHash? x, ClassInfoWithHash? y)
{
if (ReferenceEquals(x, y)) return true;
if (x is null || y is null) return false;
// 只比较哈希值
return x.Hash == y.Hash;
}
public int GetHashCode(ClassInfoWithHash obj)
{
return obj.Hash.GetHashCode();
}
}场景 5: 跨生成器通信
实现多个生成器之间的数据共享和通信。
csharp
// 共享数据存储
public static class GeneratorSharedData
{
private static readonly ConcurrentDictionary<string, object> _data = new();
public static void Set<T>(string key, T value)
{
_data[key] = value;
}
public static T? Get<T>(string key)
{
if (_data.TryGetValue(key, out var value) && value is T typedValue)
{
return typedValue;
}
return default;
}
}
// 生成器 A: 收集数据
[Generator]
public class DataCollectorGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"DataAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx))
.Collect();
context.RegisterSourceOutput(classInfos, (spc, classes) =>
{
// 存储数据供其他生成器使用
GeneratorSharedData.Set("CollectedClasses", classes);
// 生成索引文件
var code = GenerateIndex(classes);
spc.AddSource("DataIndex.g.cs", code);
});
}
}
// 生成器 B: 使用共享数据
[Generator]
public class DataConsumerGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"ConsumerAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx));
context.RegisterSourceOutput(classInfos, (spc, classInfo) =>
{
// 获取共享数据
var collectedClasses = GeneratorSharedData
.Get<ImmutableArray<ClassInfo>>("CollectedClasses");
if (collectedClasses.HasValue)
{
var code = GenerateCodeWithSharedData(classInfo, collectedClasses.Value);
spc.AddSource($"{classInfo.Name}.g.cs", code);
}
});
}
}🎯 真实使用场景
场景 1: 高性能序列化生成器
csharp
using Microsoft.CodeAnalysis;
using System.Text;
/// <summary>
/// 高性能序列化生成器
/// 展示如何生成优化的序列化代码
/// </summary>
[Generator]
public class SerializationGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 查找标记了 [Serializable] 的类
var serializableClasses = context.SyntaxProvider
.ForAttributeWithMetadataName(
"System.SerializableAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: ExtractSerializableClass)
.Where(x => x != null);
// 生成序列化代码
context.RegisterSourceOutput(serializableClasses, GenerateSerializationCode);
}
private SerializableClassInfo? ExtractSerializableClass(
GeneratorAttributeSyntaxContext context,
CancellationToken ct)
{
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
// 提取所有可序列化的属性
var properties = classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Where(p => p.DeclaredAccessibility == Accessibility.Public &&
p.GetMethod != null &&
p.SetMethod != null)
.Select(p => new PropertyInfo(
p.Name,
p.Type.ToDisplayString(),
IsSimpleType(p.Type)))
.ToList();
return new SerializableClassInfo(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
properties);
}
private void GenerateSerializationCode(
SourceProductionContext context,
SerializableClassInfo? info)
{
if (info == null) return;
var code = new StringBuilder();
code.AppendLine($@"
namespace {info.Namespace}
{{
partial class {info.ClassName}
{{
public void Serialize(System.IO.BinaryWriter writer)
{{");
// 生成序列化代码
foreach (var prop in info.Properties)
{
if (prop.IsSimpleType)
{
code.AppendLine($" writer.Write(this.{prop.Name});");
}
else
{
code.AppendLine($" // Complex type: {prop.Name}");
}
}
code.AppendLine(@" }
public static {info.ClassName} Deserialize(System.IO.BinaryReader reader)
{{
var obj = new {info.ClassName}();");
// 生成反序列化代码
foreach (var prop in info.Properties)
{
if (prop.IsSimpleType)
{
var readMethod = GetReadMethod(prop.TypeName);
code.AppendLine($" obj.{prop.Name} = reader.{readMethod}();");
}
}
code.AppendLine(@" return obj;
}
}
}}");
context.AddSource($"{info.ClassName}.Serialization.g.cs", code.ToString());
}
private bool IsSimpleType(ITypeSymbol type)
{
return type.SpecialType switch
{
SpecialType.System_Int32 => true,
SpecialType.System_String => true,
SpecialType.System_Boolean => true,
SpecialType.System_Double => true,
_ => false
};
}
private string GetReadMethod(string typeName)
{
return typeName switch
{
"int" => "ReadInt32",
"string" => "ReadString",
"bool" => "ReadBoolean",
"double" => "ReadDouble",
_ => "Read"
};
}
}
record SerializableClassInfo(
string ClassName,
string Namespace,
List<PropertyInfo> Properties);
record PropertyInfo(string Name, string TypeName, bool IsSimpleType);场景 2: 依赖注入容器生成器
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 依赖注入容器生成器
/// 自动生成服务注册代码
/// </summary>
[Generator]
public class DIContainerGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 查找标记了 [Service] 的类
var services = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyApp.ServiceAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: ExtractServiceInfo)
.Where(x => x != null);
// 收集所有服务
var allServices = services.Collect();
// 生成容器配置代码
context.RegisterSourceOutput(allServices, GenerateContainerConfiguration);
}
private ServiceInfo? ExtractServiceInfo(
GeneratorAttributeSyntaxContext context,
CancellationToken ct)
{
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
var attribute = context.Attributes[0];
// 获取服务生命周期
var lifetime = attribute.NamedArguments
.FirstOrDefault(a => a.Key == "Lifetime")
.Value.Value?.ToString() ?? "Transient";
// 获取实现的接口
var interfaces = classSymbol.AllInterfaces
.Select(i => i.ToDisplayString())
.ToList();
return new ServiceInfo(
classSymbol.ToDisplayString(),
interfaces,
lifetime);
}
private void GenerateContainerConfiguration(
SourceProductionContext context,
ImmutableArray<ServiceInfo?> services)
{
var code = new StringBuilder();
code.AppendLine(@"
using Microsoft.Extensions.DependencyInjection;
namespace MyApp.Generated
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddGeneratedServices(
this IServiceCollection services)
{");
foreach (var service in services)
{
if (service == null) continue;
var method = service.Lifetime switch
{
"Singleton" => "AddSingleton",
"Scoped" => "AddScoped",
_ => "AddTransient"
};
if (service.Interfaces.Any())
{
foreach (var @interface in service.Interfaces)
{
code.AppendLine($" services.{method}<{@interface}, {service.ClassName}>();");
}
}
else
{
code.AppendLine($" services.{method}<{service.ClassName}>();");
}
}
code.AppendLine(@" return services;
}
}
}");
context.AddSource("ServiceCollectionExtensions.g.cs", code.ToString());
}
}
record ServiceInfo(string ClassName, List<string> Interfaces, string Lifetime);场景 3: 配置验证生成器
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 配置验证生成器
/// 自动生成配置类的验证代码
/// </summary>
[Generator]
public class ConfigValidationGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var configClasses = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyApp.ConfigurationAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: ExtractConfigInfo)
.Where(x => x != null);
context.RegisterSourceOutput(configClasses, GenerateValidationCode);
}
private ConfigInfo? ExtractConfigInfo(
GeneratorAttributeSyntaxContext context,
CancellationToken ct)
{
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
var properties = classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Where(p => p.DeclaredAccessibility == Accessibility.Public)
.Select(p => new ConfigPropertyInfo(
p.Name,
p.Type.ToDisplayString(),
HasRequiredAttribute(p),
GetValidationRules(p)))
.ToList();
return new ConfigInfo(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
properties);
}
private void GenerateValidationCode(
SourceProductionContext context,
ConfigInfo? info)
{
if (info == null) return;
var code = $@"
namespace {info.Namespace}
{{
partial class {info.ClassName}
{{
public System.Collections.Generic.List<string> Validate()
{{
var errors = new System.Collections.Generic.List<string>();
{GenerateValidationLogic(info.Properties)}
return errors;
}}
}}
}}";
context.AddSource($"{info.ClassName}.Validation.g.cs", code);
}
private string GenerateValidationLogic(List<ConfigPropertyInfo> properties)
{
var sb = new StringBuilder();
foreach (var prop in properties)
{
if (prop.IsRequired)
{
if (prop.TypeName == "string")
{
sb.AppendLine($" if (string.IsNullOrEmpty(this.{prop.Name}))");
sb.AppendLine($" errors.Add(\"{prop.Name} is required\");");
}
else
{
sb.AppendLine($" if (this.{prop.Name} == null)");
sb.AppendLine($" errors.Add(\"{prop.Name} is required\");");
}
}
foreach (var rule in prop.ValidationRules)
{
sb.AppendLine($" // Validation rule: {rule}");
}
}
return sb.ToString();
}
private bool HasRequiredAttribute(IPropertySymbol property)
{
return property.GetAttributes()
.Any(a => a.AttributeClass?.Name == "RequiredAttribute");
}
private List<string> GetValidationRules(IPropertySymbol property)
{
return property.GetAttributes()
.Where(a => a.AttributeClass?.Name.EndsWith("Attribute") == true)
.Select(a => a.AttributeClass!.Name)
.ToList();
}
}
record ConfigInfo(string ClassName, string Namespace, List<ConfigPropertyInfo> Properties);
record ConfigPropertyInfo(string Name, string TypeName, bool IsRequired, List<string> ValidationRules);🔑 关键要点
- 多阶段生成 - 分析依赖关系,按顺序生成代码
- 动态模板 - 使用外部模板文件提高灵活性
- 条件编译 - 根据配置生成不同的代码
- 智能缓存 - 使用哈希值优化缓存策略
- 跨生成器通信 - 实现生成器之间的数据共享
📚 相关资源
📝 下一步
📝 文档质量保证
本文档遵循以下质量标准:
- ✅ 完整的目录结构
- ✅ 所有代码示例包含详细中文注释
- ✅ 包含最佳实践和反模式对比
- ✅ 包含真实使用场景
- ✅ 包含跨文档引用
- ✅ 内容完整,未因任何限制而精简
最后更新: 2025-01-21