Skip to content

代码生成 API 中级

⏱️ 15-20 分钟 | 📚 中级级别

🎯 学习目标

完成本指南后,你将能够:

  • [ ] 使用 SyntaxFactory 生成复杂代码
  • [ ] 掌握代码格式化技巧
  • [ ] 生成泛型类和方法
  • [ ] 处理特性和注释
  • [ ] 实现代码构建器模式

📖 前置知识

在开始之前,你应该:

🔧 常见模式

模式 1:使用 SyntaxFactory 生成类

用途: 使用类型安全的方式生成复杂代码结构

何时使用: 代码结构复杂或需要动态构建时

示例:

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

public class SyntaxFactoryClassGenerator
{
    public string GenerateClass(string className)
    {
        // 创建类声明
        var classDeclaration = ClassDeclaration(className)
            .AddModifiers(
                Token(SyntaxKind.PublicKeyword),
                Token(SyntaxKind.PartialKeyword))
            .AddMembers(
                // 添加属性
                PropertyDeclaration(
                    PredefinedType(Token(SyntaxKind.StringKeyword)),
                    Identifier("Name"))
                .AddModifiers(Token(SyntaxKind.PublicKeyword))
                .AddAccessorListAccessors(
                    AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                        .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                    AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                        .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))),
                
                // 添加方法
                MethodDeclaration(
                    PredefinedType(Token(SyntaxKind.StringKeyword)),
                    Identifier("GetInfo"))
                .AddModifiers(Token(SyntaxKind.PublicKeyword))
                .WithBody(Block(
                    ReturnStatement(
                        LiteralExpression(
                            SyntaxKind.StringLiteralExpression,
                            Literal("Info"))))));
        
        // 创建命名空间
        var namespaceDeclaration = NamespaceDeclaration(
            ParseName("Generated"))
            .AddMembers(classDeclaration);
        
        // 格式化并转换为字符串
        var code = namespaceDeclaration
            .NormalizeWhitespace()
            .ToFullString();
        
        return code;
    }
}

关键要点:

  • 使用 static using 简化代码
  • 链式调用构建语法树
  • 使用 NormalizeWhitespace() 格式化代码

模式 2:生成泛型类

用途: 生成带类型参数的泛型类

何时使用: 需要生成集合包装器、工厂类等

示例:

csharp
using Microsoft.CodeAnalysis;

public class GenericClassGenerator
{
    public string GenerateGenericWrapper()
    {
        var code = @"
using System;

namespace Generated
{
    public class Wrapper<T> where T : class
    {
        private T _value;
        
        public Wrapper(T value)
        {
            _value = value ?? throw new ArgumentNullException(nameof(value));
        }
        
        public T Value => _value;
        
        public TResult Transform<TResult>(Func<T, TResult> transformer)
        {
            return transformer(_value);
        }
    }
}";
        return code;
    }
}

模式 3:添加特性到生成的代码

用途: 为生成的类、方法添加特性标记

何时使用: 需要标记生成的代码或添加元数据时

示例:

csharp
public class AttributeGenerator
{
    public string GenerateClassWithAttributes(string className)
    {
        var code = $@"
using System;
using System.CodeDom.Compiler;

namespace Generated
{{
    [GeneratedCode(""MyGenerator"", ""1.0.0"")]
    [System.Diagnostics.DebuggerNonUserCode]
    [System.Runtime.CompilerServices.CompilerGenerated]
    public partial class {className}
    {{
        [Obsolete(""Use NewMethod instead"")]
        public void OldMethod()
        {{
            // 旧方法实现
        }}
        
        public void NewMethod()
        {{
            // 新方法实现
        }}
    }}
}}";
        return code;
    }
}

关键要点:

  • [GeneratedCode] 标记代码为生成的
  • [DebuggerNonUserCode] 在调试时跳过
  • [CompilerGenerated] 标记为编译器生成

💡 实际场景

场景 1:生成 Builder 模式类

问题: 需要为复杂对象生成 Builder 模式

解决方案:

csharp
public class BuilderPatternGenerator
{
    public string GenerateBuilder(string className, (string Type, string Name)[] properties)
    {
        var propertyFields = string.Join("\n        ", 
            properties.Select(p => $"private {p.Type} _{p.Name.ToLower()};"));
        
        var withMethods = string.Join("\n\n        ", 
            properties.Select(p => $@"public {className}Builder With{p.Name}({p.Type} value)
        {{
            _{p.Name.ToLower()} = value;
            return this;
        }}"));
        
        var buildAssignments = string.Join(",\n                ", 
            properties.Select(p => $"{p.Name} = _{p.Name.ToLower()}"));
        
        var code = $@"
namespace Generated
{{
    public class {className}Builder
    {{
        {propertyFields}
        
        {withMethods}
        
        public {className} Build()
        {{
            return new {className}
            {{
                {buildAssignments}
            }};
        }}
    }}
}}";
        return code;
    }
}

场景 2:生成序列化辅助方法

问题: 为类生成 JSON 序列化/反序列化方法

解决方案:

csharp
public class SerializationGenerator
{
    public string GenerateSerializationMethods(string className)
    {
        var code = $@"
using System.Text.Json;

namespace Generated
{{
    public partial class {className}
    {{
        public string ToJson()
        {{
            return JsonSerializer.Serialize(this, new JsonSerializerOptions
            {{
                WriteIndented = true
            }});
        }}
        
        public static {className} FromJson(string json)
        {{
            return JsonSerializer.Deserialize<{className}>(json);
        }}
        
        public {className} Clone()
        {{
            var json = ToJson();
            return FromJson(json);
        }}
    }}
}}";
        return code;
    }
}

🚀 性能优化

优化技巧 1:使用 StringBuilder 构建大型代码

问题: 字符串拼接在大型代码生成中效率低

解决方案:

csharp
using System.Text;

public class OptimizedCodeGenerator
{
    public string GenerateLargeClass(int propertyCount)
    {
        var sb = new StringBuilder();
        
        sb.AppendLine("namespace Generated");
        sb.AppendLine("{");
        sb.AppendLine("    public class LargeClass");
        sb.AppendLine("    {");
        
        for (int i = 0; i < propertyCount; i++)
        {
            sb.AppendLine($"        public string Property{i} {{ get; set; }}");
        }
        
        sb.AppendLine("    }");
        sb.AppendLine("}");
        
        return sb.ToString();
    }
}

优化技巧 2:缓存常用的语法节点

问题: 重复创建相同的语法节点浪费资源

解决方案:

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

public class CachedSyntaxGenerator
{
    // 缓存常用的修饰符
    private static readonly SyntaxToken PublicKeyword = Token(SyntaxKind.PublicKeyword);
    private static readonly SyntaxToken PrivateKeyword = Token(SyntaxKind.PrivateKeyword);
    private static readonly SyntaxToken StaticKeyword = Token(SyntaxKind.StaticKeyword);
    
    // 缓存常用的类型
    private static readonly TypeSyntax StringType = PredefinedType(Token(SyntaxKind.StringKeyword));
    private static readonly TypeSyntax IntType = PredefinedType(Token(SyntaxKind.IntKeyword));
    
    public PropertyDeclarationSyntax CreateProperty(string name, TypeSyntax type)
    {
        return PropertyDeclaration(type, Identifier(name))
            .AddModifiers(PublicKeyword)
            .AddAccessorListAccessors(
                AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
    }
}

⚠️ 常见陷阱

陷阱 1:忘记格式化生成的代码

问题: 生成的代码没有正确的缩进和换行

错误示例:

csharp
// 生成的代码难以阅读
var code = classDeclaration.ToFullString();
// 输出: public class MyClass{public string Name{get;set;}}

正确方法:

csharp
// 使用 NormalizeWhitespace() 格式化
var code = classDeclaration.NormalizeWhitespace().ToFullString();
// 输出: 
// public class MyClass
// {
//     public string Name { get; set; }
// }

陷阱 2:字符串中的转义字符处理不当

问题: 生成包含特殊字符的字符串时出错

错误示例:

csharp
var code = $@"
public string GetPath()
{{
    return ""C:\Users\Admin""; // 错误:\U 和 \A 会被解释为转义序列
}}";

正确方法:

csharp
var code = $@"
public string GetPath()
{{
    return @""C:\Users\Admin""; // 使用 @ 字符串
}}";

🔍 深入探讨

主题 1:代码格式化选项

可以自定义格式化选项:

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Formatting;

public class CustomFormattingGenerator
{
    public string GenerateWithCustomFormatting(SyntaxNode node, Workspace workspace)
    {
        var options = workspace.Options
            .WithChangedOption(FormattingOptions.IndentationSize, LanguageNames.CSharp, 2)
            .WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, false);
        
        var formatted = Formatter.Format(node, workspace, options);
        return formatted.ToFullString();
    }
}

主题 2:使用 Roslyn 模板

可以解析现有代码作为模板:

csharp
using Microsoft.CodeAnalysis.CSharp;

public class TemplateBasedGenerator
{
    public string GenerateFromTemplate(string className)
    {
        // 解析模板代码
        var template = @"
public class Template
{
    public string Property { get; set; }
}";
        
        var tree = CSharpSyntaxTree.ParseText(template);
        var root = tree.GetRoot();
        
        // 修改模板
        // ... 使用 SyntaxRewriter 修改节点
        
        return root.NormalizeWhitespace().ToFullString();
    }
}

⏭️ 下一步

🔗 相关资源

📚 在学习路径中使用

此 API 在以下步骤中使用:

基于 MIT 许可发布