如何获取程序集中所有类型的源代码/SyntaxTree(Roslyn分析器测试用)
我之前也遇到过类似的困扰——用System.Reflection加载程序集后,根本拿不到源代码或语法树相关的信息,毕竟Assembly对象是针对编译后的IL字节码设计的,和Roslyn的语法树完全不在一个层面上。下面给你两种可行的解决方案,取决于你有没有对应的调试符号(PDB文件):
方案一:借助PDB文件关联到SyntaxTree
如果你的程序集有对应的PDB文件(Debug编译默认会生成),可以通过Roslyn的API加载程序集和符号信息,进而关联到类型对应的SyntaxTree和源代码。
先确保你的项目引用了Microsoft.CodeAnalysis和Microsoft.CodeAnalysis.CSharp NuGet包,然后试试这段代码:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Reflection; using System.IO; // 获取目标程序集的物理路径 string assemblyPath = typeof(TypeWithinAssembly).Assembly.Location; // 对应的PDB文件路径(默认和程序集同目录,后缀为.pdb) string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb"); // 创建包含符号信息的元数据引用 var metadataReference = MetadataReference.CreateFromFile( assemblyPath, new MetadataReferenceProperties(), pdbPath); // 创建临时编译对象,用于访问符号信息 var compilation = CSharpCompilation.Create("TempTestCompilation") .AddReferences(metadataReference); // 获取程序集的符号对象 if (compilation.GetAssemblyOrModuleSymbol(metadataReference) is IAssemblySymbol assemblySymbol) { // 遍历程序集里的所有类型(包括嵌套类型) foreach (var typeSymbol in assemblySymbol.GlobalNamespace .GetTypeMembers() .SelectMany(t => t.GetTypeMembers()) .Concat(assemblySymbol.GlobalNamespace.GetTypeMembers())) { // 遍历类型的所有源码位置 foreach (var location in typeSymbol.Locations) { if (location.SourceTree != null) { SyntaxTree syntaxTree = location.SourceTree; string filePath = syntaxTree.FilePath; string sourceCode = syntaxTree.GetText().ToString(); // 这里就拿到了类型对应的文件路径和源代码 Console.WriteLine($"类型: {typeSymbol.Name}, 所在文件: {filePath}"); } } } }
方案二:直接从项目文件加载Compilation(适合测试自己的分析器)
如果你的测试目标是自己开发的项目,更高效的方式是直接用Roslyn加载项目文件,获取完整的Compilation对象,这样可以直接拿到所有的SyntaxTree,不需要依赖编译后的程序集和PDB。
代码示例:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.MSBuild; // 创建MSBuild工作区 using var workspace = MSBuildWorkspace.Create(); // 加载你的项目文件(替换成实际的.csproj路径) var project = await workspace.OpenProjectAsync(@"C:\YourProject\YourProject.csproj"); // 获取项目的编译对象 var compilation = await project.GetCompilationAsync(); if (compilation != null) { // 遍历项目里的所有SyntaxTree foreach (var syntaxTree in compilation.SyntaxTrees) { string filePath = syntaxTree.FilePath; string sourceCode = syntaxTree.GetText().ToString(); // 从语法树中提取所有类型声明 var root = await syntaxTree.GetRootAsync(); var typeDeclarations = root.DescendantNodes().OfType<TypeDeclarationSyntax>(); foreach (var typeDecl in typeDeclarations) { // 获取类型的语义符号 var typeSymbol = compilation.GetSemanticModel(syntaxTree).GetDeclaredSymbol(typeDecl); if (typeSymbol != null) { Console.WriteLine($"类型: {typeSymbol.Name}, 所在文件: {filePath}"); } } } }
一些关键注意事项
- 方案一中,必须保证PDB文件和程序集是同一编译输出的,否则符号信息不匹配,拿不到正确的源码位置。
- 发布版本的程序集默认可能生成精简版PDB,或者直接省略源码位置信息,测试时建议用Debug编译。
- 使用
MSBuildWorkspace时,需要确保你的环境有正确的MSBuild支持(比如安装了Visual Studio,或者在项目中引用了Microsoft.CodeAnalysis.MSBuildNuGet包)。
内容的提问来源于stack exchange,提问作者5Wagga
相关产品推荐
相关产品推荐

