编译 API 最佳实践
本文档总结编译 API 的最佳实践、常见错误和性能优化技巧。
📋 文档信息
| 属性 | 值 |
|---|---|
| 难度 | 中级 |
| 阅读时间 | 25 分钟 |
| 前置知识 | 编译基础 |
| 相关文档 | 高级技术、编译创建 |
🎯 学习目标
- ✅ 掌握编译 API 最佳实践
- ✅ 避免常见错误
- ✅ 优化编译性能
- ✅ 正确处理编译结果
最佳实践
✅ 推荐做法 1: 缓存 Compilation 对象
csharp
// ✅ 好:缓存编译对象,避免重复创建
public class CompilationCache
{
private CSharpCompilation _cachedCompilation;
public CSharpCompilation GetOrCreateCompilation()
{
if (_cachedCompilation == null)
{
_cachedCompilation = CreateCompilation();
}
return _cachedCompilation;
}
}原因: 创建 Compilation 对象开销较大,缓存可以提高性能。
✅ 推荐做法 2: 使用 GetTypeByMetadataName 查找类型
csharp
// ✅ 好:使用 GetTypeByMetadataName
var listType = compilation.GetTypeByMetadataName(
"System.Collections.Generic.List`1");原因: 这是查找类型最高效的方式。
✅ 推荐做法 3: 正确处理编译错误
csharp
// ✅ 好:检查并处理编译错误
var result = compilation.Emit(stream);
if (!result.Success)
{
var errors = result.Diagnostics
.Where(d => d.Severity == DiagnosticSeverity.Error);
foreach (var error in errors)
{
Console.WriteLine(error.GetMessage());
}
}原因: 编译可能失败,必须检查结果并处理错误。
反模式
❌ 反模式 1: 重复创建 Compilation
csharp
// ❌ 错误:每次都创建新的编译
public void ProcessMultipleTrees(List<SyntaxTree> trees)
{
foreach (var tree in trees)
{
var compilation = CSharpCompilation.Create("Temp")
.AddSyntaxTrees(tree);
// 处理...
}
}问题: 重复创建编译对象浪费资源。
正确做法: 创建一次编译,然后添加所有语法树。
❌ 反模式 2: 忘记添加必要的引用
csharp
// ❌ 错误:没有添加必要的引用
var compilation = CSharpCompilation.Create("MyAssembly")
.AddSyntaxTrees(tree);
// 编译会失败,因为缺少基本类型的引用问题: 缺少引用会导致编译失败。
正确做法: 添加所有必要的引用,至少包括 System.Runtime。
常见错误
🐛 错误 1: 缺少必要的程序集引用
描述: 编译时出现 CS0246 错误(找不到类型或命名空间)。
解决方案:
csharp
// 添加必要的引用
var references = new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
};
var compilation = CSharpCompilation.Create("MyAssembly")
.AddSyntaxTrees(tree)
.AddReferences(references);🐛 错误 2: 泛型类型的元数据名称错误
错误示例:
csharp
// ❌ 错误:泛型类型需要使用反引号和参数数量
var listType = compilation.GetTypeByMetadataName(
"System.Collections.Generic.List");
// 返回 null解决方案:
csharp
// ✅ 正确:使用反引号和参数数量
var listType = compilation.GetTypeByMetadataName(
"System.Collections.Generic.List`1");性能优化技巧
技巧 1: 重用 Compilation 对象
csharp
// ✅ 好:重用编译对象
private CSharpCompilation _baseCompilation;
public void Initialize()
{
_baseCompilation = CSharpCompilation.Create(
"BaseAssembly",
references: GetCommonReferences());
}
public CSharpCompilation AddCode(string code)
{
var tree = CSharpSyntaxTree.ParseText(code);
return _baseCompilation.AddSyntaxTrees(tree);
}技巧 2: 缓存 MetadataReference
csharp
// ✅ 好:缓存元数据引用
private static readonly Dictionary<string, MetadataReference> _referenceCache = new();
public MetadataReference GetReference(string path)
{
if (!_referenceCache.TryGetValue(path, out var reference))
{
reference = MetadataReference.CreateFromFile(path);
_referenceCache[path] = reference;
}
return reference;
}常见问题解答(FAQ)
Q1: 如何创建一个最简单的可编译的 Compilation?
A: 需要至少包含语法树和基本引用:
csharp
var syntaxTree = CSharpSyntaxTree.ParseText("class C { }");
var references = new[] {
MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
};
var compilation = CSharpCompilation.Create(
"MyAssembly",
new[] { syntaxTree },
references);Q2: 为什么编译失败提示找不到类型?
A: 通常是因为缺少必要的程序集引用。确保添加了所有需要的引用。
Q3: 如何查找泛型类型?
A: 使用反引号和参数数量:
csharp
// 查找 List<T>
var listType = compilation.GetTypeByMetadataName(
"System.Collections.Generic.List`1");关键要点
- ✅ 始终缓存 Compilation 和 MetadataReference 对象
- ✅ 使用 GetTypeByMetadataName 查找类型
- ✅ 检查 EmitResult 的 Success 属性
- ✅ 处理编译诊断信息
- ✅ 使用增量编译提高性能
- ❌ 不要重复创建 Compilation 对象
- ❌ 不要忘记添加必要的引用
- ❌ 不要忽略编译错误
相关文档
最后更新: 2026-02-06