最佳实践
📋 文档信息
难度: 🟡 中级
预计阅读时间: 25 分钟
前置知识:
- SyntaxFactory 基础和高级
- 代码构建模式
- 格式化和美化
适合人群:
- 所有代码生成器开发者
- 需要优化代码生成性能的开发者
- 需要调试代码生成器的开发者
📋 快速导航
| 章节 | 难度 | 阅读时间 | 链接 |
|---|---|---|---|
| 推荐做法 | 🟡 | 8 分钟 | 查看 |
| 反模式 | 🟡 | 5 分钟 | 查看 |
| 性能优化 | 🔴 | 7 分钟 | 查看 |
| 调试技巧 | 🟡 | 5 分钟 | 查看 |
🎯 概览
本文档总结了代码生成的最佳实践、常见错误、性能优化技巧和调试方法。
本文档涵盖:
- 推荐的代码生成做法
- 应避免的反模式
- 性能优化技巧
- 调试和故障排除方法
典型应用场景:
- 优化代码生成器性能
- 避免常见错误
- 调试复杂的代码生成逻辑
推荐做法
做法 1: 使用 NormalizeWhitespace 格式化代码
csharp
// ✅ 好的做法:使用 NormalizeWhitespace
public string GenerateClass(string className)
{
var classDecl = ClassDeclaration(className)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(/* ... */);
// 格式化代码
var formatted = classDecl.NormalizeWhitespace();
return formatted.ToFullString();
}原因:自动格式化代码,保持一致的代码风格。
做法 2: 使用 using static 简化代码
csharp
// ✅ 好的做法:使用 using static
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
public class CodeGenerator
{
public ClassDeclarationSyntax Generate()
{
// 不需要写 SyntaxFactory.ClassDeclaration
return ClassDeclaration("MyClass")
.AddModifiers(Token(SyntaxKind.PublicKeyword));
}
}原因:减少代码冗余,提高可读性。
做法 3: 使用辅助方法封装常见模式
csharp
// ✅ 好的做法:封装常见模式
public class CodeGenerationHelpers
{
/// <summary>
/// 创建公共自动属性
/// </summary>
public static PropertyDeclarationSyntax CreateAutoProperty(
string typeName,
string propertyName)
{
return PropertyDeclaration(
ParseTypeName(typeName),
Identifier(propertyName))
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
}
/// <summary>
/// 使用辅助方法
/// </summary>
public ClassDeclarationSyntax GenerateClass()
{
return ClassDeclaration("Person")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(
CreateAutoProperty("string", "Name"),
CreateAutoProperty("int", "Age"));
}
}原因:提高代码复用性和可维护性。
做法 4: 使用构建器模式组织复杂逻辑
csharp
// ✅ 好的做法:使用构建器模式
public class ClassBuilder
{
private string _className;
private List<PropertyDeclarationSyntax> _properties = new();
public ClassBuilder WithName(string name)
{
_className = name;
return this;
}
public ClassBuilder AddProperty(string type, string name)
{
var property = CreateAutoProperty(type, name);
_properties.Add(property);
return this;
}
public ClassDeclarationSyntax Build()
{
return ClassDeclaration(_className)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(_properties.ToArray());
}
}原因:使复杂的代码生成逻辑更清晰、更易维护。
做法 5: 添加 XML 文档注释
csharp
// ✅ 好的做法:添加文档注释
public ClassDeclarationSyntax GenerateDocumentedClass()
{
var classDecl = ClassDeclaration("Person")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.WithLeadingTrivia(
TriviaList(
Comment("/// <summary>"),
CarriageReturnLineFeed,
Comment("/// 表示一个人的信息"),
CarriageReturnLineFeed,
Comment("/// </summary>"),
CarriageReturnLineFeed));
return classDecl;
}原因:提高生成代码的可维护性和可读性。
反模式
反模式 1: 不格式化生成的代码
csharp
// ❌ 不好的做法:不格式化代码
public string GenerateClass(string className)
{
var classDecl = ClassDeclaration(className)
.AddModifiers(Token(SyntaxKind.PublicKeyword));
// 直接返回,没有格式化
return classDecl.ToFullString();
}
// 结果:public class MyClass{} // 没有换行和缩进问题:生成的代码难以阅读。
正确做法:使用 NormalizeWhitespace(参见推荐做法 1)。
反模式 2: 硬编码字符串拼接
csharp
// ❌ 不好的做法:硬编码字符串拼接
public string GenerateMethod()
{
return "public void MyMethod() { Console.WriteLine(\"Hello\"); }";
}问题:难以维护,容易出错,缺少语法验证。
正确做法:使用 SyntaxFactory 或 ParseStatement。
反模式 3: 忘记添加分号标记
csharp
// ❌ 不好的做法:缺少分号标记
var accessor = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration);
// 结果:get // 缺少分号问题:生成的代码语法不正确。
正确做法:
csharp
// ✅ 正确:添加分号标记
var accessor = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
// 结果:get;反模式 4: 逐个添加成员
csharp
// ❌ 不好的做法:逐个添加成员
public ClassDeclarationSyntax CreateClass(List<PropertyInfo> properties)
{
var classDecl = ClassDeclaration("MyClass")
.AddModifiers(Token(SyntaxKind.PublicKeyword));
foreach (var prop in properties)
{
classDecl = classDecl.AddMembers(CreateProperty(prop)); // 每次创建新对象
}
return classDecl;
}问题:性能低下,每次都创建新对象。
正确做法:
csharp
// ✅ 好:一次性添加所有成员
public ClassDeclarationSyntax CreateClass(List<PropertyInfo> properties)
{
var members = properties
.Select(p => CreateProperty(p))
.ToArray();
return ClassDeclaration("MyClass")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(members); // 一次性添加
}性能优化技巧
技巧 1: 重用 SyntaxToken
csharp
// ✅ 好:重用常用的 token
public class TokenCache
{
private static readonly SyntaxToken PublicKeyword =
Token(SyntaxKind.PublicKeyword);
private static readonly SyntaxToken PrivateKeyword =
Token(SyntaxKind.PrivateKeyword);
private static readonly SyntaxToken SemicolonToken =
Token(SyntaxKind.SemicolonToken);
public PropertyDeclarationSyntax CreateProperty(string name)
{
return PropertyDeclaration(
PredefinedType(Token(SyntaxKind.StringKeyword)),
name)
.AddModifiers(PublicKeyword) // 重用 token
.AddAccessorListAccessors(
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SemicolonToken), // 重用 token
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SemicolonToken)); // 重用 token
}
}原因:避免重复创建相同的 token,提高性能。
技巧 2: 批量添加成员
csharp
// ✅ 好:一次性添加所有成员
public ClassDeclarationSyntax CreateClass(List<PropertyInfo> properties)
{
var members = properties
.Select(p => CreateProperty(p))
.ToArray();
return ClassDeclaration("MyClass")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(members); // 一次性添加
}
// ❌ 不好:逐个添加成员
public ClassDeclarationSyntax CreateClassSlow(List<PropertyInfo> properties)
{
var classDecl = ClassDeclaration("MyClass")
.AddModifiers(Token(SyntaxKind.PublicKeyword));
foreach (var prop in properties)
{
classDecl = classDecl.AddMembers(CreateProperty(prop)); // 每次创建新对象
}
return classDecl;
}原因:减少对象创建次数,提高性能。
技巧 3: 延迟格式化
csharp
// ✅ 好:最后才格式化
public string GenerateMultipleClasses(List<string> classNames)
{
var classes = classNames
.Select(name => ClassDeclaration(name)
.AddModifiers(Token(SyntaxKind.PublicKeyword)))
.ToArray();
var compilationUnit = CompilationUnit()
.AddMembers(classes);
// 只在最后格式化一次
return compilationUnit.NormalizeWhitespace().ToFullString();
}
// ❌ 不好:每次都格式化
public string GenerateMultipleClassesSlow(List<string> classNames)
{
var result = new StringBuilder();
foreach (var name in classNames)
{
var classDecl = ClassDeclaration(name)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.NormalizeWhitespace(); // 每次都格式化
result.AppendLine(classDecl.ToFullString());
}
return result.ToString();
}原因:格式化是昂贵的操作,应该只在最后执行一次。
技巧 4: 使用 StringBuilder 拼接大量代码
csharp
// ✅ 好:对于简单的代码片段,使用 StringBuilder
public string GenerateManySimpleProperties(List<string> propertyNames)
{
var sb = new StringBuilder();
foreach (var name in propertyNames)
{
sb.AppendLine($"public string {name} {{ get; set; }}");
}
return sb.ToString();
}原因:对于简单的代码片段,字符串拼接比 SyntaxFactory 更快。
技巧 5: 缓存常用的语法节点
csharp
public class SyntaxNodeCache
{
// 缓存常用的类型
private static readonly PredefinedTypeSyntax StringType =
PredefinedType(Token(SyntaxKind.StringKeyword));
private static readonly PredefinedTypeSyntax IntType =
PredefinedType(Token(SyntaxKind.IntKeyword));
private static readonly PredefinedTypeSyntax BoolType =
PredefinedType(Token(SyntaxKind.BoolKeyword));
// 缓存常用的访问器
private static readonly AccessorDeclarationSyntax GetAccessor =
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
private static readonly AccessorDeclarationSyntax SetAccessor =
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
public PropertyDeclarationSyntax CreateStringProperty(string name)
{
return PropertyDeclaration(StringType, name)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(GetAccessor, SetAccessor);
}
}原因:避免重复创建相同的语法节点,提高性能。
调试技巧
技巧 1: 使用 Syntax Visualizer
Roslyn 提供了 Syntax Visualizer 工具,可以帮助你理解语法树的结构。
安装方法:
- 在 Visual Studio 中,打开"扩展" > "管理扩展"
- 搜索 ".NET Compiler Platform SDK"
- 安装后,在"视图" > "其他窗口"中找到"Syntax Visualizer"
使用方法:
- 打开任何 C# 文件
- 在 Syntax Visualizer 中查看语法树结构
- 点击节点查看其类型和属性
- 复制节点的创建代码
技巧 2: 输出生成的代码进行检查
csharp
/// <summary>
/// 调试辅助方法:输出生成的代码
/// </summary>
public class CodeGenerationDebugger
{
public void DebugGeneratedCode(SyntaxNode node)
{
// 输出未格式化的代码
Console.WriteLine("=== 未格式化 ===");
Console.WriteLine(node.ToFullString());
Console.WriteLine();
// 输出格式化的代码
Console.WriteLine("=== 格式化后 ===");
Console.WriteLine(node.NormalizeWhitespace().ToFullString());
Console.WriteLine();
// 输出语法树结构
Console.WriteLine("=== 语法树结构 ===");
PrintSyntaxTree(node, 0);
}
private void PrintSyntaxTree(SyntaxNode node, int indent)
{
var indentStr = new string(' ', indent * 2);
Console.WriteLine($"{indentStr}{node.GetType().Name}: {node.Kind()}");
foreach (var child in node.ChildNodes())
{
PrintSyntaxTree(child, indent + 1);
}
}
}技巧 3: 使用单元测试验证生成结果
csharp
using Xunit;
using Microsoft.CodeAnalysis.CSharp;
/// <summary>
/// 代码生成单元测试
/// </summary>
public class CodeGenerationTests
{
[Fact]
public void GenerateClass_ShouldCreateValidClass()
{
// Arrange
var generator = new ClassGenerator();
// Act
var classDecl = generator.GenerateClass("Person");
var code = classDecl.NormalizeWhitespace().ToFullString();
// Assert
Assert.Contains("public class Person", code);
// 验证生成的代码可以编译
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var compilation = CSharpCompilation.Create("Test")
.AddSyntaxTrees(syntaxTree);
var diagnostics = compilation.GetDiagnostics();
Assert.Empty(diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error));
}
}技巧 4: 使用断点调试
csharp
/// <summary>
/// 在关键位置设置断点
/// </summary>
public ClassDeclarationSyntax GenerateClass()
{
var classDecl = ClassDeclaration("MyClass");
// 设置断点,检查 classDecl 的状态
classDecl = classDecl.AddModifiers(Token(SyntaxKind.PublicKeyword));
// 设置断点,检查添加修饰符后的状态
var property = CreateProperty("Name");
// 设置断点,检查属性创建结果
classDecl = classDecl.AddMembers(property);
// 设置断点,检查最终结果
return classDecl;
}💡 实用技巧总结
代码质量
- 始终格式化代码: 使用
NormalizeWhitespace() - 添加文档注释: 使用 XML 文档注释
- 使用有意义的命名: 变量和方法名要清晰
- 封装常用操作: 创建辅助方法和扩展方法
性能优化
- 重用 token 和节点: 缓存常用的语法元素
- 批量操作: 一次性添加多个成员
- 延迟格式化: 只在最后格式化一次
- 选择合适的方法: 简单场景用字符串,复杂场景用 SyntaxFactory
调试和测试
- 使用 Syntax Visualizer: 理解语法树结构
- 输出中间结果: 检查生成的代码
- 编写单元测试: 验证生成结果
- 使用断点调试: 跟踪代码生成过程
⚠️ 常见错误总结
语法错误
- 忘记添加分号: 使用
WithSemicolonToken - 缺少修饰符: 使用
AddModifiers - 类型参数错误: 检查泛型类型的类型参数
格式错误
- 没有格式化: 使用
NormalizeWhitespace() - 缩进不一致: 使用统一的缩进设置
- 缺少换行: 检查 trivia 设置
性能问题
- 重复创建对象: 重用常用的 token 和节点
- 逐个添加成员: 批量添加成员
- 频繁格式化: 只在最后格式化一次
🔗 相关文档
- 返回索引
- SyntaxFactory 基础 - 学习 SyntaxFactory 的基本使用
- SyntaxFactory 高级 - 学习复杂结构的生成技术
- 代码构建模式 - 学习常用的代码构建模式
- 格式化和美化 - 学习如何格式化生成的代码
其他相关文档
官方资源
- Roslyn GitHub - Roslyn 源代码
- Roslyn API 文档 - 官方 API 文档
- Syntax Visualizer - 语法可视化工具
📚 下一步
学习完本文档后,建议:
- 实践应用: 在实际项目中应用这些最佳实践
- 性能测试: 测量和优化代码生成器的性能
- 持续学习: 关注 Roslyn 的最新更新和最佳实践
最后更新: 2026-02-05
文档版本: 1.0