编译创建和配置
本文档详细介绍如何创建和配置 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