Skip to content

编译创建和配置

本文档详细介绍如何创建和配置 CSharpCompilation 对象,包括语法树管理、编译选项配置和完整示例。


📋 文档信息

属性
难度中级
阅读时间20 分钟
前置知识C# 基础、Roslyn 基本概念
相关文档元数据引用管理编译选项

🎯 学习目标

完成本文档学习后,你将能够:

  • ✅ 创建基本的 CSharpCompilation 对象
  • ✅ 管理编译中的语法树
  • ✅ 配置编译选项
  • ✅ 创建可执行的编译实例

📚 快速导航

主题描述
CSharpCompilation.Create 方法创建编译对象的基本方法
添加语法树管理编译中的语法树
编译选项配置编译行为
完整示例创建可编译和执行的完整示例

CSharpCompilation.Create 方法

CSharpCompilation 是 Roslyn 中表示 C# 编译的核心类。通过 Create 方法可以创建一个新的编译实例。

创建最简单的编译

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Generic;

/// <summary>
/// 演示如何创建 CSharpCompilation
/// </summary>
public class CompilationCreationDemo
{
    /// <summary>
    /// 创建最简单的编译
    /// </summary>
    public CSharpCompilation CreateSimpleCompilation()
    {
        // 创建一个空的编译
        // assemblyName: 程序集名称
        var compilation = CSharpCompilation.Create("MyAssembly");
        
        return compilation;
    }
    
    /// <summary>
    /// 创建带语法树的编译
    /// </summary>
    public CSharpCompilation CreateCompilationWithSyntaxTree(string sourceCode)
    {
        // 解析源代码为语法树
        var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
        
        // 创建编译并添加语法树
        var compilation = CSharpCompilation.Create(
            assemblyName: "MyAssembly",
            syntaxTrees: new[] { syntaxTree });
        
        return compilation;
    }
    
    /// <summary>
    /// 创建带引用的编译
    /// </summary>
    public CSharpCompilation CreateCompilationWithReferences(
        string sourceCode,
        IEnumerable<MetadataReference> references)
    {
        // 解析源代码
        var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
        
        // 创建编译,指定语法树和引用
        var compilation = CSharpCompilation.Create(
            assemblyName: "MyAssembly",
            syntaxTrees: new[] { syntaxTree },
            references: references);
        
        return compilation;
    }
    
    /// <summary>
    /// 创建带选项的编译
    /// </summary>
    public CSharpCompilation CreateCompilationWithOptions(
        string sourceCode,
        CSharpCompilationOptions options)
    {
        var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
        
        // 创建编译,指定编译选项
        var compilation = CSharpCompilation.Create(
            assemblyName: "MyAssembly",
            syntaxTrees: new[] { syntaxTree },
            options: options);
        
        return compilation;
    }
}

添加语法树

编译可以包含多个语法树,每个语法树代表一个源文件。

语法树管理

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// 演示如何管理编译中的语法树
/// </summary>
public class SyntaxTreeManagement
{
    /// <summary>
    /// 向编译添加语法树
    /// </summary>
    public CSharpCompilation AddSyntaxTree(
        CSharpCompilation compilation,
        SyntaxTree syntaxTree)
    {
        // 编译是不可变的,AddSyntaxTrees 返回新的编译实例
        return compilation.AddSyntaxTrees(syntaxTree);
    }
    
    /// <summary>
    /// 向编译添加多个语法树
    /// </summary>
    public CSharpCompilation AddMultipleSyntaxTrees(
        CSharpCompilation compilation,
        IEnumerable<SyntaxTree> syntaxTrees)
    {
        // 一次添加多个语法树
        return compilation.AddSyntaxTrees(syntaxTrees);
    }
    
    /// <summary>
    /// 从编译移除语法树
    /// </summary>
    public CSharpCompilation RemoveSyntaxTree(
        CSharpCompilation compilation,
        SyntaxTree syntaxTree)
    {
        // 移除指定的语法树
        return compilation.RemoveSyntaxTrees(syntaxTree);
    }
    
    /// <summary>
    /// 替换编译中的语法树
    /// </summary>
    public CSharpCompilation ReplaceSyntaxTree(
        CSharpCompilation compilation,
        SyntaxTree oldTree,
        SyntaxTree newTree)
    {
        // 替换语法树(用于增量编译场景)
        return compilation.ReplaceSyntaxTree(oldTree, newTree);
    }
    
    /// <summary>
    /// 获取编译中的所有语法树
    /// </summary>
    public IEnumerable<SyntaxTree> GetAllSyntaxTrees(CSharpCompilation compilation)
    {
        // 返回编译中的所有语法树
        return compilation.SyntaxTrees;
    }
    
    /// <summary>
    /// 检查编译是否包含特定语法树
    /// </summary>
    public bool ContainsSyntaxTree(
        CSharpCompilation compilation,
        SyntaxTree syntaxTree)
    {
        return compilation.ContainsSyntaxTree(syntaxTree);
    }
}

编译选项

CSharpCompilationOptions 用于配置编译行为。

基本编译选项

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

/// <summary>
/// 演示如何配置编译选项
/// </summary>
public class CompilationOptionsDemo
{
    /// <summary>
    /// 创建基本的编译选项
    /// </summary>
    public CSharpCompilationOptions CreateBasicOptions()
    {
        // 创建控制台应用程序的编译选项
        var options = new CSharpCompilationOptions(
            outputKind: OutputKind.ConsoleApplication);
        
        return options;
    }
    
    /// <summary>
    /// 创建类库的编译选项
    /// </summary>
    public CSharpCompilationOptions CreateLibraryOptions()
    {
        // 创建类库的编译选项
        var options = new CSharpCompilationOptions(
            outputKind: OutputKind.DynamicallyLinkedLibrary);
        
        return options;
    }
    
    /// <summary>
    /// 创建带优化的编译选项
    /// </summary>
    public CSharpCompilationOptions CreateOptimizedOptions()
    {
        var options = new CSharpCompilationOptions(
            outputKind: OutputKind.ConsoleApplication,
            // 启用优化
            optimizationLevel: OptimizationLevel.Release,
            // 允许不安全代码
            allowUnsafe: false,
            // 平台目标
            platform: Platform.AnyCpu);
        
        return options;
    }
    
    /// <summary>
    /// 创建调试模式的编译选项
    /// </summary>
    public CSharpCompilationOptions CreateDebugOptions()
    {
        var options = new CSharpCompilationOptions(
            outputKind: OutputKind.ConsoleApplication,
            // 调试模式(不优化)
            optimizationLevel: OptimizationLevel.Debug,
            // 生成完整的调试信息
            deterministic: true);
        
        return options;
    }
    
    /// <summary>
    /// 修改现有的编译选项
    /// </summary>
    public CSharpCompilationOptions ModifyOptions(
        CSharpCompilationOptions options)
    {
        // 编译选项是不可变的,使用 With 方法创建新实例
        return options
            .WithOptimizationLevel(OptimizationLevel.Release)
            .WithAllowUnsafe(true)
            .WithPlatform(Platform.X64);
    }
}

编译创建完整示例

以下是一个完整的示例,展示如何创建一个可以编译和执行的编译:

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

/// <summary>
/// 完整的编译创建和执行示例
/// </summary>
public class CompleteCompilationExample
{
    /// <summary>
    /// 创建、编译并执行代码
    /// </summary>
    public void CompileAndExecute()
    {
        // 1. 准备源代码
        string sourceCode = @"
using System;

namespace MyNamespace
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine(""Hello from Roslyn!"");
        }
    }
}";
        
        // 2. 解析源代码为语法树
        var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
        
        // 3. 添加必要的程序集引用
        var references = new[]
        {
            // 引用 .NET 核心库
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
            MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location),
            MetadataReference.CreateFromFile(Assembly.Load("System.Console").Location)
        };
        
        // 4. 创建编译选项
        var options = new CSharpCompilationOptions(
            outputKind: OutputKind.ConsoleApplication,
            optimizationLevel: OptimizationLevel.Release);
        
        // 5. 创建编译
        var compilation = CSharpCompilation.Create(
            assemblyName: "MyDynamicAssembly",
            syntaxTrees: new[] { syntaxTree },
            references: references,
            options: options);
        
        // 6. 编译到内存流
        using var ms = new MemoryStream();
        EmitResult result = compilation.Emit(ms);
        
        // 7. 检查编译结果
        if (!result.Success)
        {
            // 编译失败,输出错误
            var failures = result.Diagnostics
                .Where(diagnostic => diagnostic.IsWarningAsError ||
                                   diagnostic.Severity == DiagnosticSeverity.Error);
            
            foreach (var diagnostic in failures)
            {
                Console.Error.WriteLine($"{diagnostic.Id}: {diagnostic.GetMessage()}");
            }
            
            return;
        }
        
        // 8. 编译成功,加载并执行程序集
        ms.Seek(0, SeekOrigin.Begin);
        Assembly assembly = Assembly.Load(ms.ToArray());
        
        // 9. 查找并调用 Main 方法
        Type programType = assembly.GetType("MyNamespace.Program");
        MethodInfo mainMethod = programType.GetMethod("Main");
        mainMethod.Invoke(null, null);
    }
    
    /// <summary>
    /// 编译到文件
    /// </summary>
    public bool CompileToFile(string sourceCode, string outputPath)
    {
        // 解析源代码
        var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
        
        // 添加引用
        var references = new[]
        {
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
        };
        
        // 创建编译
        var compilation = CSharpCompilation.Create(
            assemblyName: Path.GetFileNameWithoutExtension(outputPath),
            syntaxTrees: new[] { syntaxTree },
            references: references,
            options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));
        
        // 编译到文件
        EmitResult result = compilation.Emit(outputPath);
        
        // 返回编译是否成功
        return result.Success;
    }
}

最佳实践

✅ 推荐做法

做法 1: 缓存 Compilation 对象

csharp
// ✅ 好:缓存编译对象,避免重复创建
public class CompilationCache
{
    private CSharpCompilation _cachedCompilation;
    
    public CSharpCompilation GetOrCreateCompilation()
    {
        if (_cachedCompilation == null)
        {
            _cachedCompilation = CreateCompilation();
        }
        return _cachedCompilation;
    }
}

原因: 创建 Compilation 对象开销较大,缓存可以提高性能。

做法 2: 正确处理编译错误

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。


关键要点

  • CSharpCompilation.Create 是创建编译的核心方法
  • ✅ 编译对象是不可变的,所有修改操作返回新实例
  • ✅ 语法树代表源文件,一个编译可以包含多个语法树
  • ✅ 编译选项控制输出类型、优化级别等行为
  • ✅ 必须添加必要的程序集引用才能成功编译
  • ✅ 使用 Emit 方法将编译结果输出到文件或内存

相关文档


下一步


最后更新: 2026-02-06

基于 MIT 许可发布