Skip to content

格式化和美化

📋 文档信息

难度: 🟢 简单
预计阅读时间: 15 分钟
前置知识:

  • SyntaxFactory 基础
  • C# 基础语法

适合人群:

  • 所有代码生成器开发者
  • 需要生成格式化代码的开发者

📋 快速导航

章节难度阅读时间链接
NormalizeWhitespace🟢5 分钟查看
自定义格式化🟡7 分钟查看
完整示例🟡3 分钟查看

🎯 概览

本文档介绍如何格式化和美化生成的代码,使其具有良好的可读性和一致的风格。

本文档涵盖:

  • 使用 NormalizeWhitespace 自动格式化
  • 自定义缩进和换行
  • 添加 XML 文档注释
  • 添加区域标记

典型应用场景:

  • 生成格式化良好的代码
  • 添加文档注释
  • 组织代码结构

NormalizeWhitespace

NormalizeWhitespace 方法是格式化生成代码的最简单方式。

基本格式化

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

/// <summary>
/// 代码格式化示例
/// </summary>
public class CodeFormattingExamples
{
    /// <summary>
    /// 基本的格式化
    /// </summary>
    public string BasicFormatting()
    {
        // 创建一个没有格式化的类
        var classDecl = ClassDeclaration("MyClass")
            .AddModifiers(Token(SyntaxKind.PublicKeyword))
            .AddMembers(
                PropertyDeclaration(
                    PredefinedType(Token(SyntaxKind.StringKeyword)),
                    "Name")
                .AddModifiers(Token(SyntaxKind.PublicKeyword))
                .AddAccessorListAccessors(
                    AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                        .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                    AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                        .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))));
        
        // 使用 NormalizeWhitespace 格式化
        var formatted = classDecl.NormalizeWhitespace();
        
        // 转换为字符串
        return formatted.ToFullString();
        
        // 输出:
        // public class MyClass
        // {
        //     public string Name { get; set; }
        // }
    }
}

自定义缩进

csharp
/// <summary>
/// 自定义缩进格式化
/// </summary>
public string CustomIndentFormatting()
{
    var classDecl = ClassDeclaration("MyClass")
        .AddModifiers(Token(SyntaxKind.PublicKeyword))
        .AddMembers(
            PropertyDeclaration(
                PredefinedType(Token(SyntaxKind.StringKeyword)),
                "Name")
            .AddModifiers(Token(SyntaxKind.PublicKeyword))
            .AddAccessorListAccessors(
                AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))));
    
    // 使用自定义缩进(4个空格)
    var formatted = classDecl.NormalizeWhitespace(indentation: "    ");
    
    return formatted.ToFullString();
}

格式化整个编译单元

csharp
/// <summary>
/// 格式化整个编译单元
/// </summary>
public string FormatCompilationUnit()
{
    // 创建完整的编译单元
    var compilationUnit = CompilationUnit()
        .AddUsings(
            UsingDirective(ParseName("System")),
            UsingDirective(ParseName("System.Collections.Generic")))
        .AddMembers(
            NamespaceDeclaration(ParseName("MyNamespace"))
            .AddMembers(
                ClassDeclaration("MyClass")
                .AddModifiers(Token(SyntaxKind.PublicKeyword))
                .AddMembers(
                    PropertyDeclaration(
                        PredefinedType(Token(SyntaxKind.StringKeyword)),
                        "Name")
                    .AddModifiers(Token(SyntaxKind.PublicKeyword))
                    .AddAccessorListAccessors(
                        AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                            .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                        AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                            .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))))));
    
    // 格式化整个编译单元
    var formatted = compilationUnit.NormalizeWhitespace();
    
    return formatted.ToFullString();
    
    // 输出:
    // using System;
    // using System.Collections.Generic;
    //
    // namespace MyNamespace
    // {
    //     public class MyClass
    //     {
    //         public string Name { get; set; }
    //     }
    // }
}

自定义格式化

添加 XML 文档注释

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

/// <summary>
/// 自定义格式化示例
/// </summary>
public class CustomFormatting
{
    /// <summary>
    /// 添加自定义注释
    /// </summary>
    public ClassDeclarationSyntax AddXmlDocumentation(
        ClassDeclarationSyntax classDecl,
        string summary)
    {
        // 创建 XML 文档注释
        var xmlComment = Comment($"/// <summary>\n/// {summary}\n/// </summary>");
        
        // 添加注释到类声明
        return classDecl.WithLeadingTrivia(
            TriviaList(xmlComment, CarriageReturnLineFeed));
    }
    
    /// <summary>
    /// 为属性添加文档注释
    /// </summary>
    public PropertyDeclarationSyntax AddPropertyDocumentation(
        PropertyDeclarationSyntax property,
        string summary)
    {
        return property.WithLeadingTrivia(
            TriviaList(
                CarriageReturnLineFeed,
                Whitespace("    "),
                Comment("/// <summary>"),
                CarriageReturnLineFeed,
                Whitespace("    "),
                Comment($"/// {summary}"),
                CarriageReturnLineFeed,
                Whitespace("    "),
                Comment("/// </summary>"),
                CarriageReturnLineFeed,
                Whitespace("    ")));
    }
}

添加区域标记

csharp
/// <summary>
/// 添加区域标记
/// </summary>
public ClassDeclarationSyntax AddRegions(ClassDeclarationSyntax classDecl)
{
    // 创建 #region 和 #endregion
    var regionStart = Trivia(
        RegionDirectiveTrivia(true)
        .WithEndOfDirectiveToken(
            Token(TriviaList(PreprocessingMessage("Properties")),
                SyntaxKind.EndOfDirectiveToken,
                TriviaList())));
    
    var regionEnd = Trivia(EndRegionDirectiveTrivia(true));
    
    // 添加区域到类成员
    var members = classDecl.Members;
    if (members.Any())
    {
        var firstMember = members.First()
            .WithLeadingTrivia(regionStart);
        var lastMember = members.Last()
            .WithTrailingTrivia(regionEnd);
        
        classDecl = classDecl.WithMembers(
            List(members.Select((m, i) =>
                i == 0 ? firstMember :
                i == members.Count - 1 ? lastMember : m)));
    }
    
    return classDecl;
}

格式化完整示例

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using System.Text;

/// <summary>
/// 完整的格式化示例
/// </summary>
public class CompleteFormattingExample
{
    /// <summary>
    /// 生成格式化良好的类
    /// </summary>
    public string GenerateWellFormattedClass()
    {
        // 1. 创建类
        var classDecl = ClassDeclaration("Person")
            .AddModifiers(Token(SyntaxKind.PublicKeyword));
        
        // 2. 添加 XML 文档注释
        classDecl = classDecl.WithLeadingTrivia(
            TriviaList(
                Comment("/// <summary>"),
                CarriageReturnLineFeed,
                Comment("/// 表示一个人的信息"),
                CarriageReturnLineFeed,
                Comment("/// </summary>"),
                CarriageReturnLineFeed));
        
        // 3. 添加属性
        var properties = new[]
        {
            CreateDocumentedProperty("string", "FirstName", "名字"),
            CreateDocumentedProperty("string", "LastName", "姓氏"),
            CreateDocumentedProperty("int", "Age", "年龄")
        };
        
        classDecl = classDecl.AddMembers(properties);
        
        // 4. 创建命名空间
        var namespaceDecl = NamespaceDeclaration(ParseName("MyApp.Models"))
            .AddMembers(classDecl);
        
        // 5. 创建编译单元
        var compilationUnit = CompilationUnit()
            .AddUsings(UsingDirective(ParseName("System")))
            .AddMembers(namespaceDecl);
        
        // 6. 格式化
        var formatted = compilationUnit.NormalizeWhitespace();
        
        return formatted.ToFullString();
    }
    
    /// <summary>
    /// 创建带文档注释的属性
    /// </summary>
    private PropertyDeclarationSyntax CreateDocumentedProperty(
        string type,
        string name,
        string summary)
    {
        var property = PropertyDeclaration(
            ParseTypeName(type),
            Identifier(name))
        .AddModifiers(Token(SyntaxKind.PublicKeyword))
        .AddAccessorListAccessors(
            AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
            AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
        
        // 添加 XML 文档注释
        property = property.WithLeadingTrivia(
            TriviaList(
                CarriageReturnLineFeed,
                Whitespace("    "),
                Comment("/// <summary>"),
                CarriageReturnLineFeed,
                Whitespace("    "),
                Comment($"/// {summary}"),
                CarriageReturnLineFeed,
                Whitespace("    "),
                Comment("/// </summary>"),
                CarriageReturnLineFeed,
                Whitespace("    ")));
        
        return property;
    }
}

💡 最佳实践

实践 1: 始终格式化生成的代码

csharp
// ✅ 好:格式化代码
public string GenerateCode()
{
    var classDecl = ClassDeclaration("MyClass")
        .AddModifiers(Token(SyntaxKind.PublicKeyword));
    
    // 格式化
    var formatted = classDecl.NormalizeWhitespace();
    return formatted.ToFullString();
}

// ❌ 差:不格式化
public string GenerateCode()
{
    var classDecl = ClassDeclaration("MyClass")
        .AddModifiers(Token(SyntaxKind.PublicKeyword));
    
    // 直接返回,没有格式化
    return classDecl.ToFullString();
    // 结果:public class MyClass{}  // 没有换行和缩进
}

原因: 格式化的代码更易读,更符合编码规范。

实践 2: 添加有意义的文档注释

csharp
// ✅ 好:添加文档注释
var classDecl = ClassDeclaration("Person")
    .WithLeadingTrivia(
        TriviaList(
            Comment("/// <summary>"),
            CarriageReturnLineFeed,
            Comment("/// 表示一个人的信息"),
            CarriageReturnLineFeed,
            Comment("/// </summary>"),
            CarriageReturnLineFeed));

// ❌ 差:没有文档注释
var classDecl = ClassDeclaration("Person");

原因: 文档注释提高代码的可维护性和可读性。

实践 3: 使用一致的缩进

csharp
// ✅ 好:使用一致的缩进
var formatted = classDecl.NormalizeWhitespace(indentation: "    ");

// ❌ 差:混合使用不同的缩进
// 有些地方用 2 个空格,有些地方用 4 个空格

原因: 一致的缩进使代码更整洁,更易读。


⚠️ 常见错误

错误 1: 忘记格式化代码

问题: 生成的代码没有格式化,难以阅读。

csharp
// ❌ 错误:没有格式化
var code = classDecl.ToFullString();
// 结果:public class MyClass{public string Name{get;set;}}

解决方案:

csharp
// ✅ 正确:使用 NormalizeWhitespace
var code = classDecl.NormalizeWhitespace().ToFullString();
// 结果:格式化的代码

错误 2: 文档注释格式不正确

问题: XML 文档注释的格式不正确,导致 IDE 无法识别。

csharp
// ❌ 错误:注释格式不正确
var classDecl = ClassDeclaration("MyClass")
    .WithLeadingTrivia(
        TriviaList(Comment("// This is my class")));  // 使用普通注释

解决方案:

csharp
// ✅ 正确:使用 XML 文档注释
var classDecl = ClassDeclaration("MyClass")
    .WithLeadingTrivia(
        TriviaList(
            Comment("/// <summary>"),
            CarriageReturnLineFeed,
            Comment("/// This is my class"),
            CarriageReturnLineFeed,
            Comment("/// </summary>"),
            CarriageReturnLineFeed));

错误 3: 缩进不一致

问题: 生成的代码缩进不一致。

csharp
// ❌ 错误:手动添加缩进,容易出错
var code = "public class MyClass\n{\n  public string Name { get; set; }\n}";

解决方案:

csharp
// ✅ 正确:使用 NormalizeWhitespace 自动处理缩进
var formatted = classDecl.NormalizeWhitespace();
var code = formatted.ToFullString();

🔗 相关文档

其他相关文档


📚 下一步

学习完本文档后,建议继续学习:

  1. 代码构建模式 - 学习如何组织复杂的代码生成逻辑
  2. 最佳实践 - 掌握代码生成的最佳实践和性能优化
  3. 实战示例 - 查看完整的源生成器示例

最后更新: 2026-02-05
文档版本: 1.0

基于 MIT 许可发布