编译 API 高级
⏱️ 30-45 分钟 | 📚 高级级别
🎯 学习目标
完成本指南后,你将能够:
- [ ] 实现跨程序集符号查找
- [ ] 掌握增量编译技术
- [ ] 进行编译性能优化
- [ ] 处理复杂的编译场景
- [ ] 实现高级诊断分析
📖 前置知识
在开始之前,你应该:
🔧 高级技术
技术 1:跨程序集符号查找
用途: 在多个程序集中查找和解析类型符号
何时使用: 需要分析依赖关系或查找外部类型时
示例:
csharp
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Linq;
public class CrossAssemblySymbolLookup
{
// 在所有引用的程序集中查找类型
public INamedTypeSymbol FindTypeAcrossAssemblies(
Compilation compilation,
string fullyQualifiedName)
{
// 方法 1: 使用 GetTypeByMetadataName(最快)
var type = compilation.GetTypeByMetadataName(fullyQualifiedName);
if (type != null)
{
return type;
}
// 方法 2: 手动搜索所有程序集
foreach (var reference in compilation.References)
{
var assembly = compilation.GetAssemblyOrModuleSymbol(reference)
as IAssemblySymbol;
if (assembly != null)
{
type = FindTypeInAssembly(assembly, fullyQualifiedName);
if (type != null)
{
return type;
}
}
}
return null;
}
// 在特定程序集中查找类型
private INamedTypeSymbol FindTypeInAssembly(
IAssemblySymbol assembly,
string typeName)
{
return FindTypeInNamespace(assembly.GlobalNamespace, typeName);
}
// 在命名空间中递归查找类型
private INamedTypeSymbol FindTypeInNamespace(
INamespaceSymbol namespaceSymbol,
string typeName)
{
// 提取类型名称(不含命名空间)
var parts = typeName.Split('.');
var simpleTypeName = parts[^1];
// 在当前命名空间中查找
var type = namespaceSymbol.GetTypeMembers(simpleTypeName).FirstOrDefault();
if (type != null && type.ToDisplayString() == typeName)
{
return type;
}
// 在子命名空间中递归查找
foreach (var childNamespace in namespaceSymbol.GetNamespaceMembers())
{
type = FindTypeInNamespace(childNamespace, typeName);
if (type != null)
{
return type;
}
}
return null;
}
// 查找实现特定接口的所有类型
public List<INamedTypeSymbol> FindTypesImplementingInterface(
Compilation compilation,
string interfaceName)
{
var results = new List<INamedTypeSymbol>();
var interfaceType = compilation.GetTypeByMetadataName(interfaceName);
if (interfaceType == null)
{
return results;
}
// 遍历所有程序集
foreach (var reference in compilation.References)
{
var assembly = compilation.GetAssemblyOrModuleSymbol(reference)
as IAssemblySymbol;
if (assembly != null)
{
var types = GetAllTypesFromAssembly(assembly);
results.AddRange(types.Where(t =>
t.AllInterfaces.Contains(interfaceType, SymbolEqualityComparer.Default)));
}
}
return results;
}
// 获取程序集中的所有类型
private IEnumerable<INamedTypeSymbol> GetAllTypesFromAssembly(
IAssemblySymbol assembly)
{
return GetAllTypesFromNamespace(assembly.GlobalNamespace);
}
// 从命名空间递归获取所有类型
private IEnumerable<INamedTypeSymbol> GetAllTypesFromNamespace(
INamespaceSymbol namespaceSymbol)
{
foreach (var type in namespaceSymbol.GetTypeMembers())
{
yield return type;
// 递归获取嵌套类型
foreach (var nestedType in GetNestedTypes(type))
{
yield return nestedType;
}
}
foreach (var childNamespace in namespaceSymbol.GetNamespaceMembers())
{
foreach (var type in GetAllTypesFromNamespace(childNamespace))
{
yield return type;
}
}
}
// 获取嵌套类型
private IEnumerable<INamedTypeSymbol> GetNestedTypes(INamedTypeSymbol type)
{
foreach (var nestedType in type.GetTypeMembers())
{
yield return nestedType;
foreach (var deeperNested in GetNestedTypes(nestedType))
{
yield return deeperNested;
}
}
}
}关键要点:
GetTypeByMetadataName()是最快的查找方式- 需要提供完全限定名(包括命名空间)
- 泛型类型需要添加
`N后缀(N 是类型参数数量) - 手动搜索适用于复杂查找场景
性能考虑:
- 优先使用
GetTypeByMetadataName() - 缓存查找结果避免重复搜索
- 使用并行处理加速大规模搜索
技术 2:增量编译
用途: 在不重新创建整个编译的情况下更新部分代码
何时使用: 需要频繁更新代码并保持编译状态时
示例:
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Generic;
public class IncrementalCompilationManager
{
private CSharpCompilation _currentCompilation;
private Dictionary<string, SyntaxTree> _syntaxTreeCache = new();
// 初始化基础编译
public void Initialize(
string assemblyName,
IEnumerable<MetadataReference> references)
{
_currentCompilation = CSharpCompilation.Create(
assemblyName: assemblyName,
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary));
}
// 添加新文件
public CSharpCompilation AddFile(string fileName, string sourceCode)
{
var syntaxTree = CSharpSyntaxTree.ParseText(
sourceCode,
path: fileName);
_syntaxTreeCache[fileName] = syntaxTree;
_currentCompilation = _currentCompilation.AddSyntaxTrees(syntaxTree);
return _currentCompilation;
}
// 更新现有文件
public CSharpCompilation UpdateFile(string fileName, string sourceCode)
{
if (!_syntaxTreeCache.TryGetValue(fileName, out var oldTree))
{
// 文件不存在,添加新文件
return AddFile(fileName, sourceCode);
}
// 解析新代码
var newTree = CSharpSyntaxTree.ParseText(
sourceCode,
path: fileName);
// 替换旧的语法树
_currentCompilation = _currentCompilation.ReplaceSyntaxTree(
oldTree,
newTree);
_syntaxTreeCache[fileName] = newTree;
return _currentCompilation;
}
// 删除文件
public CSharpCompilation RemoveFile(string fileName)
{
if (_syntaxTreeCache.TryGetValue(fileName, out var tree))
{
_currentCompilation = _currentCompilation.RemoveSyntaxTrees(tree);
_syntaxTreeCache.Remove(fileName);
}
return _currentCompilation;
}
// 批量更新文件
public CSharpCompilation UpdateMultipleFiles(
Dictionary<string, string> fileUpdates)
{
var treesToRemove = new List<SyntaxTree>();
var treesToAdd = new List<SyntaxTree>();
foreach (var (fileName, sourceCode) in fileUpdates)
{
// 如果文件已存在,标记旧树为删除
if (_syntaxTreeCache.TryGetValue(fileName, out var oldTree))
{
treesToRemove.Add(oldTree);
}
// 创建新树
var newTree = CSharpSyntaxTree.ParseText(
sourceCode,
path: fileName);
treesToAdd.Add(newTree);
_syntaxTreeCache[fileName] = newTree;
}
// 批量更新
_currentCompilation = _currentCompilation
.RemoveSyntaxTrees(treesToRemove)
.AddSyntaxTrees(treesToAdd);
return _currentCompilation;
}
// 获取当前编译
public CSharpCompilation GetCurrentCompilation()
{
return _currentCompilation;
}
// 获取文件的语法树
public SyntaxTree GetSyntaxTree(string fileName)
{
_syntaxTreeCache.TryGetValue(fileName, out var tree);
return tree;
}
}关键要点:
- 使用
ReplaceSyntaxTree()更新单个文件 - 批量更新时一次性调用
RemoveSyntaxTrees()和AddSyntaxTrees() - 缓存语法树以便快速访问
- Compilation 是不可变的,每次操作返回新实例
性能优势:
- 避免重新解析未更改的文件
- 保留已计算的语义信息
- 减少内存分配
技术 3:编译性能优化
用途: 提升大型项目的编译性能
何时使用: 编译时间成为瓶颈时
示例:
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
public class CompilationPerformanceOptimizer
{
// 缓存编译对象
private static readonly ConcurrentDictionary<string, CSharpCompilation>
_compilationCache = new();
// 缓存元数据引用
private static readonly ConcurrentDictionary<string, MetadataReference>
_referenceCache = new();
// 缓存语义模型
private static readonly ConcurrentDictionary<SyntaxTree, SemanticModel>
_semanticModelCache = new();
// 优化 1: 缓存编译对象
public CSharpCompilation GetOrCreateCompilation(
string assemblyName,
IEnumerable<SyntaxTree> syntaxTrees,
IEnumerable<MetadataReference> references)
{
var cacheKey = GenerateCacheKey(assemblyName, syntaxTrees);
return _compilationCache.GetOrAdd(cacheKey, _ =>
{
return CSharpCompilation.Create(
assemblyName: assemblyName,
syntaxTrees: syntaxTrees,
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary));
});
}
// 优化 2: 缓存元数据引用
public MetadataReference GetOrCreateReference(string assemblyPath)
{
return _referenceCache.GetOrAdd(assemblyPath, path =>
{
return MetadataReference.CreateFromFile(path);
});
}
// 优化 3: 缓存语义模型
public SemanticModel GetOrCreateSemanticModel(
Compilation compilation,
SyntaxTree syntaxTree)
{
return _semanticModelCache.GetOrAdd(syntaxTree, tree =>
{
return compilation.GetSemanticModel(tree);
});
}
// 优化 4: 并行处理语法树
public void ProcessSyntaxTreesInParallel(
Compilation compilation,
Action<SyntaxTree, SemanticModel> processor)
{
var syntaxTrees = compilation.SyntaxTrees.ToList();
System.Threading.Tasks.Parallel.ForEach(syntaxTrees, tree =>
{
var semanticModel = compilation.GetSemanticModel(tree);
processor(tree, semanticModel);
});
}
// 优化 5: 最小化引用集
public IEnumerable<MetadataReference> GetMinimalReferences(
string sourceCode)
{
var references = new List<MetadataReference>();
// 始终需要的基础引用
references.Add(GetOrCreateReference(
typeof(object).Assembly.Location));
// 根据代码内容动态添加引用
if (sourceCode.Contains("Console"))
{
references.Add(GetOrCreateReference(
typeof(Console).Assembly.Location));
}
if (sourceCode.Contains("Linq") ||
sourceCode.Contains("Select") ||
sourceCode.Contains("Where"))
{
references.Add(GetOrCreateReference(
typeof(Enumerable).Assembly.Location));
}
if (sourceCode.Contains("Task") || sourceCode.Contains("async"))
{
references.Add(GetOrCreateReference(
typeof(System.Threading.Tasks.Task).Assembly.Location));
}
return references;
}
// 性能测量
public CompilationPerformanceMetrics MeasurePerformance(
CSharpCompilation compilation)
{
var metrics = new CompilationPerformanceMetrics();
var stopwatch = Stopwatch.StartNew();
// 测量语义模型创建时间
stopwatch.Restart();
foreach (var tree in compilation.SyntaxTrees)
{
var model = compilation.GetSemanticModel(tree);
}
metrics.SemanticModelCreationTime = stopwatch.Elapsed;
// 测量诊断分析时间
stopwatch.Restart();
var diagnostics = compilation.GetDiagnostics();
metrics.DiagnosticAnalysisTime = stopwatch.Elapsed;
// 测量 Emit 时间
stopwatch.Restart();
using var ms = new System.IO.MemoryStream();
var result = compilation.Emit(ms);
metrics.EmitTime = stopwatch.Elapsed;
metrics.Success = result.Success;
return metrics;
}
// 生成缓存键
private string GenerateCacheKey(
string assemblyName,
IEnumerable<SyntaxTree> syntaxTrees)
{
var treeHashes = string.Join(",",
syntaxTrees.Select(t => t.GetRoot().GetHashCode()));
return $"{assemblyName}_{treeHashes}";
}
// 清理缓存
public void ClearCaches()
{
_compilationCache.Clear();
_referenceCache.Clear();
_semanticModelCache.Clear();
}
}
public class CompilationPerformanceMetrics
{
public TimeSpan SemanticModelCreationTime { get; set; }
public TimeSpan DiagnosticAnalysisTime { get; set; }
public TimeSpan EmitTime { get; set; }
public bool Success { get; set; }
public TimeSpan TotalTime =>
SemanticModelCreationTime +
DiagnosticAnalysisTime +
EmitTime;
public override string ToString()
{
return $@"
编译性能指标:
- 语义模型创建: {SemanticModelCreationTime.TotalMilliseconds:F2}ms
- 诊断分析: {DiagnosticAnalysisTime.TotalMilliseconds:F2}ms
- Emit: {EmitTime.TotalMilliseconds:F2}ms
- 总时间: {TotalTime.TotalMilliseconds:F2}ms
- 成功: {Success}";
}
}关键要点:
- 缓存 Compilation、MetadataReference 和 SemanticModel
- 使用并行处理加速多文件分析
- 最小化引用集减少加载时间
- 测量性能找出瓶颈
性能提升:
- 缓存可提升 50-80% 的性能
- 并行处理可提升 2-4 倍速度(取决于 CPU 核心数)
- 最小化引用可减少 30-50% 的加载时间
💡 实际场景
场景 1:构建代码分析工具
问题: 需要构建一个分析整个解决方案的工具
解决方案:
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class SolutionAnalyzer
{
public AnalysisResult AnalyzeSolution(string solutionPath)
{
var result = new AnalysisResult();
// 1. 加载解决方案中的所有项目
var projects = LoadProjects(solutionPath);
// 2. 为每个项目创建编译
foreach (var project in projects)
{
var compilation = CreateCompilationForProject(project);
// 3. 分析编译
var projectAnalysis = AnalyzeCompilation(compilation);
result.ProjectAnalyses.Add(project.Name, projectAnalysis);
}
// 4. 生成跨项目分析
result.CrossProjectAnalysis = AnalyzeCrossProjectDependencies(projects);
return result;
}
private List<ProjectInfo> LoadProjects(string solutionPath)
{
var projects = new List<ProjectInfo>();
var solutionDir = Path.GetDirectoryName(solutionPath);
// 查找所有 .csproj 文件
var projectFiles = Directory.GetFiles(
solutionDir,
"*.csproj",
SearchOption.AllDirectories);
foreach (var projectFile in projectFiles)
{
projects.Add(new ProjectInfo
{
Name = Path.GetFileNameWithoutExtension(projectFile),
Path = projectFile,
SourceFiles = GetSourceFiles(Path.GetDirectoryName(projectFile))
});
}
return projects;
}
private List<string> GetSourceFiles(string projectDir)
{
return Directory.GetFiles(projectDir, "*.cs", SearchOption.AllDirectories)
.Where(f => !f.Contains("\\obj\\") && !f.Contains("\\bin\\"))
.ToList();
}
private CSharpCompilation CreateCompilationForProject(ProjectInfo project)
{
// 解析所有源文件
var syntaxTrees = project.SourceFiles
.Select(file => CSharpSyntaxTree.ParseText(
File.ReadAllText(file),
path: file))
.ToList();
// 添加标准引用
var references = GetStandardReferences();
// 创建编译
return CSharpCompilation.Create(
assemblyName: project.Name,
syntaxTrees: syntaxTrees,
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary));
}
private ProjectAnalysis AnalyzeCompilation(CSharpCompilation compilation)
{
var analysis = new ProjectAnalysis();
// 统计代码行数
analysis.TotalLines = compilation.SyntaxTrees
.Sum(t => t.GetRoot().GetText().Lines.Count);
// 统计类数量
analysis.ClassCount = compilation.SyntaxTrees
.SelectMany(t => t.GetRoot().DescendantNodes())
.OfType<ClassDeclarationSyntax>()
.Count();
// 统计方法数量
analysis.MethodCount = compilation.SyntaxTrees
.SelectMany(t => t.GetRoot().DescendantNodes())
.OfType<MethodDeclarationSyntax>()
.Count();
// 分析诊断
var diagnostics = compilation.GetDiagnostics();
analysis.ErrorCount = diagnostics.Count(d =>
d.Severity == DiagnosticSeverity.Error);
analysis.WarningCount = diagnostics.Count(d =>
d.Severity == DiagnosticSeverity.Warning);
return analysis;
}
private CrossProjectAnalysis AnalyzeCrossProjectDependencies(
List<ProjectInfo> projects)
{
// 分析项目间依赖关系
return new CrossProjectAnalysis
{
TotalProjects = projects.Count,
// 更多跨项目分析...
};
}
private IEnumerable<MetadataReference> GetStandardReferences()
{
return new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Console).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location)
};
}
}
public class ProjectInfo
{
public string Name { get; set; }
public string Path { get; set; }
public List<string> SourceFiles { get; set; }
}
public class AnalysisResult
{
public Dictionary<string, ProjectAnalysis> ProjectAnalyses { get; set; } = new();
public CrossProjectAnalysis CrossProjectAnalysis { get; set; }
}
public class ProjectAnalysis
{
public int TotalLines { get; set; }
public int ClassCount { get; set; }
public int MethodCount { get; set; }
public int ErrorCount { get; set; }
public int WarningCount { get; set; }
}
public class CrossProjectAnalysis
{
public int TotalProjects { get; set; }
}说明: 这个示例展示了如何构建一个完整的解决方案分析工具,包括加载项目、创建编译、分析代码和生成报告。
场景 2:实现编译缓存系统
问题: 需要为频繁编译的场景实现高效的缓存系统
解决方案:
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class CompilationCacheSystem
{
private readonly ConcurrentDictionary<string, CachedCompilation> _cache = new();
private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(30);
// 获取或创建编译(带缓存)
public CSharpCompilation GetOrCreateCompilation(
string assemblyName,
IEnumerable<string> sourceFiles,
IEnumerable<MetadataReference> references)
{
// 生成缓存键
var cacheKey = GenerateCacheKey(assemblyName, sourceFiles);
// 检查缓存
if (_cache.TryGetValue(cacheKey, out var cached))
{
// 检查是否过期
if (DateTime.UtcNow - cached.CreatedAt < _cacheExpiration)
{
return cached.Compilation;
}
// 过期,移除
_cache.TryRemove(cacheKey, out _);
}
// 创建新编译
var syntaxTrees = sourceFiles
.Select(file => CSharpSyntaxTree.ParseText(
System.IO.File.ReadAllText(file),
path: file))
.ToList();
var compilation = CSharpCompilation.Create(
assemblyName: assemblyName,
syntaxTrees: syntaxTrees,
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary));
// 缓存
_cache[cacheKey] = new CachedCompilation
{
Compilation = compilation,
CreatedAt = DateTime.UtcNow
};
return compilation;
}
// 生成缓存键(基于内容哈希)
private string GenerateCacheKey(
string assemblyName,
IEnumerable<string> sourceFiles)
{
var sb = new StringBuilder();
sb.Append(assemblyName);
foreach (var file in sourceFiles.OrderBy(f => f))
{
var content = System.IO.File.ReadAllText(file);
var hash = ComputeHash(content);
sb.Append(hash);
}
return ComputeHash(sb.ToString());
}
// 计算内容哈希
private string ComputeHash(string content)
{
using var sha256 = SHA256.Create();
var bytes = Encoding.UTF8.GetBytes(content);
var hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
// 清理过期缓存
public void CleanupExpiredCache()
{
var now = DateTime.UtcNow;
var expiredKeys = _cache
.Where(kvp => now - kvp.Value.CreatedAt >= _cacheExpiration)
.Select(kvp => kvp.Key)
.ToList();
foreach (var key in expiredKeys)
{
_cache.TryRemove(key, out _);
}
}
// 获取缓存统计
public CacheStatistics GetStatistics()
{
return new CacheStatistics
{
TotalCachedCompilations = _cache.Count,
OldestCacheAge = _cache.Values
.Select(c => DateTime.UtcNow - c.CreatedAt)
.DefaultIfEmpty()
.Max()
};
}
// 清空所有缓存
public void ClearAll()
{
_cache.Clear();
}
}
public class CachedCompilation
{
public CSharpCompilation Compilation { get; set; }
public DateTime CreatedAt { get; set; }
}
public class CacheStatistics
{
public int TotalCachedCompilations { get; set; }
public TimeSpan OldestCacheAge { get; set; }
}说明: 实现了一个完整的编译缓存系统,包括基于内容哈希的缓存键生成、过期管理和统计功能。
🚀 性能优化技巧
技巧 1:使用 Compilation.WithXxx() 方法
问题: 频繁创建新的 Compilation 对象影响性能
解决方案:
csharp
// 差:每次都创建新编译
var compilation1 = CSharpCompilation.Create("Assembly1", ...);
var compilation2 = CSharpCompilation.Create("Assembly2", ...);
// 好:重用基础编译
var baseCompilation = CSharpCompilation.Create("Base", ...);
var compilation1 = baseCompilation.WithAssemblyName("Assembly1");
var compilation2 = baseCompilation.WithAssemblyName("Assembly2");技巧 2:延迟获取语义模型
问题: 提前创建所有语义模型浪费资源
解决方案:
csharp
// 差:提前创建所有语义模型
var models = compilation.SyntaxTrees
.Select(t => compilation.GetSemanticModel(t))
.ToList();
// 好:按需创建
foreach (var tree in compilation.SyntaxTrees)
{
if (NeedsSemanticAnalysis(tree))
{
var model = compilation.GetSemanticModel(tree);
// 使用 model...
}
}技巧 3:批量操作语法树
问题: 多次单独添加/删除语法树效率低
解决方案:
csharp
// 差:多次单独操作
foreach (var tree in treesToAdd)
{
compilation = compilation.AddSyntaxTrees(tree);
}
// 好:批量操作
compilation = compilation.AddSyntaxTrees(treesToAdd);⚠️ 常见陷阱
陷阱 1:忘记处理循环引用
问题: 跨程序集查找时可能遇到循环引用
错误示例:
csharp
// 可能导致无限递归
private INamedTypeSymbol FindType(INamespaceSymbol ns, string name)
{
foreach (var child in ns.GetNamespaceMembers())
{
return FindType(child, name); // 没有终止条件
}
return null;
}正确方法:
csharp
private INamedTypeSymbol FindType(
INamespaceSymbol ns,
string name,
HashSet<INamespaceSymbol> visited = null)
{
visited ??= new HashSet<INamespaceSymbol>(SymbolEqualityComparer.Default);
if (!visited.Add(ns))
{
return null; // 已访问过,避免循环
}
var type = ns.GetTypeMembers(name).FirstOrDefault();
if (type != null) return type;
foreach (var child in ns.GetNamespaceMembers())
{
type = FindType(child, name, visited);
if (type != null) return type;
}
return null;
}陷阱 2:缓存过期的编译对象
问题: 使用过期的编译对象导致错误结果
错误示例:
csharp
// 缓存编译但不检查源文件是否更改
private static CSharpCompilation _cachedCompilation;
public CSharpCompilation GetCompilation(string sourceFile)
{
if (_cachedCompilation != null)
{
return _cachedCompilation; // 可能已过期
}
// ...
}正确方法:
csharp
private static CSharpCompilation _cachedCompilation;
private static DateTime _cacheTime;
private static string _cachedFilePath;
public CSharpCompilation GetCompilation(string sourceFile)
{
var fileModified = File.GetLastWriteTimeUtc(sourceFile);
if (_cachedCompilation != null &&
_cachedFilePath == sourceFile &&
_cacheTime >= fileModified)
{
return _cachedCompilation;
}
// 重新创建编译
_cachedCompilation = CreateCompilation(sourceFile);
_cacheTime = DateTime.UtcNow;
_cachedFilePath = sourceFile;
return _cachedCompilation;
}陷阱 3:不处理大型程序集的内存问题
问题: 加载大型程序集可能导致内存溢出
错误示例:
csharp
// 一次性加载所有程序集
var references = Directory.GetFiles(runtimePath, "*.dll")
.Select(f => MetadataReference.CreateFromFile(f))
.ToList();正确方法:
csharp
// 按需加载,使用弱引用
private readonly Dictionary<string, WeakReference<MetadataReference>>
_referenceCache = new();
public MetadataReference GetReference(string path)
{
if (_referenceCache.TryGetValue(path, out var weakRef) &&
weakRef.TryGetTarget(out var reference))
{
return reference;
}
reference = MetadataReference.CreateFromFile(path);
_referenceCache[path] = new WeakReference<MetadataReference>(reference);
return reference;
}🔍 深入探讨
主题 1:编译的内部工作原理
Compilation 对象在内部维护了复杂的数据结构:
符号表: 存储所有类型、方法、属性等符号 绑定器: 负责名称解析和类型检查 诊断引擎: 收集和报告编译错误
示例:
csharp
public class CompilationInternals
{
public void ExploreInternals(CSharpCompilation compilation)
{
// 1. 符号表
var assembly = compilation.Assembly;
var globalNamespace = assembly.GlobalNamespace;
// 2. 类型系统
var objectType = compilation.GetSpecialType(SpecialType.System_Object);
var stringType = compilation.GetSpecialType(SpecialType.System_String);
// 3. 诊断系统
var diagnostics = compilation.GetDiagnostics();
// 4. 元数据
var references = compilation.References;
var externalReferences = compilation.ExternalReferences;
}
}主题 2:编译选项的深层影响
不同的编译选项会影响生成的代码:
OptimizationLevel.Debug:
- 保留所有局部变量
- 不内联方法
- 生成序列点用于调试
OptimizationLevel.Release:
- 移除未使用的代码
- 内联小方法
- 优化循环
示例:
csharp
public class OptimizationImpact
{
public void CompareOptimizations()
{
var sourceCode = @"
public class Test
{
public int Add(int a, int b) => a + b;
public void UseAdd()
{
var result = Add(1, 2);
}
}";
// Debug 编译
var debugCompilation = CreateCompilation(
sourceCode,
OptimizationLevel.Debug);
// Release 编译
var releaseCompilation = CreateCompilation(
sourceCode,
OptimizationLevel.Release);
// Release 版本可能会内联 Add 方法
}
private CSharpCompilation CreateCompilation(
string code,
OptimizationLevel level)
{
var tree = CSharpSyntaxTree.ParseText(code);
return CSharpCompilation.Create(
"Test",
new[] { tree },
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: level));
}
}⏭️ 下一步
- 探索编译单元测试了解测试技术
- 查看性能分析工具
- 阅读Roslyn 内部机制深入理解
- 学习自定义编译器构建自己的编译器
🔗 相关资源
📚 在学习路径中使用
此 API 在以下步骤中使用:
- 步骤 6:高级代码分析 - 高级用法
- 步骤 7:生产就绪 - 性能优化
💬 另请参阅
📝 总结
编译 API 的高级用法包括:
- 跨程序集符号查找 - 在多个程序集中查找和解析类型
- 增量编译 - 高效更新部分代码而不重建整个编译
- 性能优化 - 通过缓存、并行处理和最小化引用提升性能
- 复杂场景处理 - 构建代码分析工具、实现编译缓存系统
掌握这些高级技术后,你将能够:
- 构建高性能的代码分析工具
- 实现复杂的代码生成场景
- 优化大型项目的编译性能
- 处理企业级的编译需求
继续探索其他高级主题,深入理解 Roslyn 的强大功能!