代码生成 API 中级
⏱️ 15-20 分钟 | 📚 中级级别
🎯 学习目标
完成本指南后,你将能够:
- [ ] 使用 SyntaxFactory 生成复杂代码
- [ ] 掌握代码格式化技巧
- [ ] 生成泛型类和方法
- [ ] 处理特性和注释
- [ ] 实现代码构建器模式
📖 前置知识
在开始之前,你应该:
- 完成 代码生成 API 基础
- 理解基本的字符串生成方式
- 熟悉 C# 语法结构
🔧 常见模式
模式 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 在以下步骤中使用:
- 步骤 4:增量生成器 - 中级用法
- 步骤 5:复杂代码生成 - 高级用法