代码生成 API 基础
⏱️ 5-10 分钟 | 📚 基础级别
🎯 学习目标
完成本指南后,你将能够:
- [ ] 理解代码生成的两种方式(字符串 vs SyntaxFactory)
- [ ] 使用字符串生成简单代码
- [ ] 使用 AddSource() 添加生成的代码
- [ ] 格式化生成的代码
- [ ] 生成常见的代码结构
📖 核心概念
什么是代码生成?
代码生成是 Source Generator 的核心功能,它允许你在编译时动态创建 C# 代码。生成的代码会被添加到编译中,就像手写的代码一样。
两种代码生成方式
1. 基于字符串的生成(推荐用于简单代码)
- 直接编写 C# 代码字符串
- 简单直观,易于理解
- 适合生成简单的类、方法
2. 使用 SyntaxFactory(用于复杂代码)
- 使用 Roslyn API 构建语法树
- 类型安全,不易出错
- 适合生成复杂的代码结构
何时使用哪种方式?
- 简单代码(< 50 行): 使用字符串
- 复杂代码(> 50 行): 使用 SyntaxFactory
- 动态结构: 使用 SyntaxFactory
- 模板代码: 使用字符串
🔧 常用方法
方法 1:使用 AddSource() 添加代码
用途: 将生成的代码添加到编译中
何时使用: 每次生成代码时都需要使用
示例:
csharp
using Microsoft.CodeAnalysis;
[Generator]
public class SimpleCodeGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 生成代码字符串
var sourceCode = @"
namespace Generated
{
public class HelloWorld
{
public string GetMessage()
{
return ""Hello from generator!"";
}
}
}";
// 添加到编译
// 参数1: 文件名(必须以 .g.cs 结尾)
// 参数2: 源代码字符串
context.AddSource("HelloWorld.g.cs", sourceCode);
}
public void Initialize(GeneratorInitializationContext context) { }
}关键要点:
- 文件名必须以
.g.cs结尾(表示生成的代码) - 文件名必须唯一,避免冲突
- 源代码必须是有效的 C# 代码
方法 2:生成简单的类
用途: 使用字符串生成基本的类结构
何时使用: 需要生成简单的 POCO 类、DTO 等
示例:
csharp
using Microsoft.CodeAnalysis;
[Generator]
public class ClassGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 定义类信息
var className = "Person";
var namespaceName = "MyApp.Models";
// 生成类代码
var code = $@"
namespace {namespaceName}
{{
public partial class {className}
{{
public string Name {{ get; set; }}
public int Age {{ get; set; }}
public string GetInfo()
{{
return $""{{Name}} is {{Age}} years old"";
}}
}}
}}";
context.AddSource($"{className}.g.cs", code);
}
public void Initialize(GeneratorInitializationContext context) { }
}关键要点:
- 使用字符串插值
$动态生成内容 - 使用双花括号转义花括号
- 建议生成
partial类,方便用户扩展
方法 3:生成带属性的类
用途: 根据数据动态生成多个属性
何时使用: 需要根据配置或元数据生成属性时
示例:
csharp
using Microsoft.CodeAnalysis;
using System.Linq;
[Generator]
public class PropertyGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 定义属性列表
var properties = new[]
{
("Name", "string"),
("Age", "int"),
("Email", "string"),
("IsActive", "bool")
};
// 生成属性代码
var propertiesCode = string.Join("\n ",
properties.Select(p =>
$"public {p.Item2} {p.Item1} {{ get; set; }}"));
// 生成完整类
var code = $@"
namespace Generated
{{
public partial class User
{{
{propertiesCode}
}}
}}";
context.AddSource("User.g.cs", code);
}
public void Initialize(GeneratorInitializationContext context) { }
}关键要点:
- 使用 LINQ 动态生成重复结构
- 使用
string.Join()组合多个代码片段 - 注意缩进和格式
方法 4:生成方法
用途: 生成方法代码
何时使用: 需要生成辅助方法、扩展方法等
示例:
csharp
using Microsoft.CodeAnalysis;
[Generator]
public class MethodGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var className = "Calculator";
// 生成多个方法
var code = $@"
namespace Generated
{{
public static class {className}
{{
public static int Add(int a, int b)
{{
return a + b;
}}
public static int Subtract(int a, int b)
{{
return a - b;
}}
public static int Multiply(int a, int b)
{{
return a * b;
}}
public static double Divide(int a, int b)
{{
if (b == 0)
throw new System.DivideByZeroException();
return (double)a / b;
}}
}}
}}";
context.AddSource($"{className}.g.cs", code);
}
public void Initialize(GeneratorInitializationContext context) { }
}关键要点:
- 方法可以包含完整的逻辑
- 注意异常处理
- 使用
static类生成工具方法
方法 5:添加 using 语句
用途: 在生成的代码中添加命名空间引用
何时使用: 生成的代码需要使用外部类型时
示例:
csharp
using Microsoft.CodeAnalysis;
using System.Linq;
[Generator]
public class UsingGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 定义需要的命名空间
var usings = new[]
{
"System",
"System.Collections.Generic",
"System.Linq",
"System.Text"
};
// 生成 using 语句
var usingStatements = string.Join("\n",
usings.Select(u => $"using {u};"));
// 生成完整代码
var code = $@"
{usingStatements}
namespace Generated
{{
public class DataProcessor
{{
public List<string> ProcessData(string[] input)
{{
return input
.Where(s => !string.IsNullOrEmpty(s))
.Select(s => s.ToUpper())
.ToList();
}}
}}
}}";
context.AddSource("DataProcessor.g.cs", code);
}
public void Initialize(GeneratorInitializationContext context) { }
}关键要点:
- using 语句放在文件开头
- 只添加必要的命名空间
- 使用
string.Join()生成多个 using
💡 实际示例
示例 1:生成 DTO 类
场景: 为数据库实体生成对应的 DTO 类
解决方案:
csharp
using Microsoft.CodeAnalysis;
[Generator]
public class DtoGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 假设我们要为 User 实体生成 UserDto
var entityName = "User";
var dtoName = $"{entityName}Dto";
var code = $@"
using System;
namespace MyApp.Dtos
{{
/// <summary>
/// 为 {entityName} 实体生成的 DTO
/// </summary>
public class {dtoName}
{{
public int Id {{ get; set; }}
public string Name {{ get; set; }}
public string Email {{ get; set; }}
public DateTime CreatedAt {{ get; set; }}
/// <summary>
/// 从实体创建 DTO
/// </summary>
public static {dtoName} FromEntity({entityName} entity)
{{
return new {dtoName}
{{
Id = entity.Id,
Name = entity.Name,
Email = entity.Email,
CreatedAt = entity.CreatedAt
}};
}}
}}
}}";
context.AddSource($"{dtoName}.g.cs", code);
}
public void Initialize(GeneratorInitializationContext context) { }
}说明:
- 生成了完整的 DTO 类
- 包含 XML 文档注释
- 提供了从实体转换的静态方法
示例 2:生成扩展方法
场景: 为字符串类型生成常用的扩展方法
解决方案:
csharp
using Microsoft.CodeAnalysis;
[Generator]
public class ExtensionMethodGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var code = @"
using System;
namespace Generated.Extensions
{
public static class StringExtensions
{
/// <summary>
/// 检查字符串是否为空或仅包含空白字符
/// </summary>
public static bool IsNullOrWhiteSpace(this string value)
{
return string.IsNullOrWhiteSpace(value);
}
/// <summary>
/// 截断字符串到指定长度
/// </summary>
public static string Truncate(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value))
return value;
return value.Length <= maxLength
? value
: value.Substring(0, maxLength) + ""..."";
}
/// <summary>
/// 反转字符串
/// </summary>
public static string Reverse(this string value)
{
if (string.IsNullOrEmpty(value))
return value;
var chars = value.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
}";
context.AddSource("StringExtensions.g.cs", code);
}
public void Initialize(GeneratorInitializationContext context) { }
}说明:
- 生成了多个实用的扩展方法
- 包含完整的错误处理
- 添加了 XML 文档注释
⏭️ 下一步
🔗 相关资源
📚 在学习路径中使用
此 API 在以下步骤中使用:
- 步骤 2:Hello World 生成器 - 基本用法
- 步骤 3:基于特性的生成 - 实际应用