Skip to content

编译 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

基于 MIT 许可发布