如何从CSharpCompilation的ISymbol同时获取所属程序集与声明语法引用
根本原因
Roslyn 编译对象的设计逻辑是:所有包含在当前 Compilation 语法树中的类型,默认归属当前编译本身的程序集,优先级高于引用程序集中的同名同签名类型,因此单编译实例无法同时返回源码定义类型的原始所属程序集和语法引用。
可行解决方案
方案1:双编译实例配对查询
该方案对现有逻辑侵入小,不需要依赖预先映射数据:
- 维护两个编译实例:
- 「引用专用编译」:仅添加所有项目的
MetadataReference,不添加任何语法树,专门用于查询类型的原始所属程序集信息 - 「语法专用编译」:按需添加语法树和所有引用,专门用于查询声明语法引用、执行代码修改
- 「引用专用编译」:仅添加所有项目的
- 按类型的完全限定元数据名做两个编译的符号匹配即可分别获取两类信息
对应你的单元测试适配代码如下:
// 引用专用编译,全程不添加语法树 var refCompilation = CSharpCompilation.Create("ref", null, new[] { systemDllRef, baseDllRef, mainDllRef }); // 语法专用编译,按需添加语法树 var syntaxCompilation = CSharpCompilation.Create("syntax", null, new[] { systemDllRef, baseDllRef, mainDllRef }); // 只修改语法专用编译的语法树 syntaxCompilation = syntaxCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(MAIN_CODE)); syntaxCompilation = syntaxCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(BASE_CODE)); // 查程序集信息走引用专用编译 var refTypeSymbol = refCompilation.GetTypeByMetadataName("T"); Assert.AreEqual("main", refTypeSymbol.ContainingAssembly.Name); Assert.AreEqual("base", refTypeSymbol.Interfaces[0].ContainingAssembly.Name); // 查语法引用走语法专用编译 var syntaxTypeSymbol = syntaxCompilation.GetTypeByMetadataName("T"); CollectionAssert.IsNotEmpty(syntaxTypeSymbol.DeclaringSyntaxReferences); CollectionAssert.IsNotEmpty(syntaxTypeSymbol.Interfaces[0].DeclaringSyntaxReferences);
方案2:基于预先映射表关联查询
该方案更适配你现有架构,性能更高:
你已经预先生成了全解决方案的类型映射,存储了每个源文件对应的所属项目(程序集)信息,只需要在创建语法树时传入文件的真实路径:
// 加载语法树时传入实际文件路径 var mainSyntaxTree = CSharpSyntaxTree.ParseText(MAIN_CODE, path: "实际项目的T类文件路径.cs"); compilation = compilation.AddSyntaxTrees(mainSyntaxTree);
拿到符号的DeclaringSyntaxReferences后,取语法树的FilePath字段,直接查预先的映射表就能得到该类型真实所属的程序集,完全不需要依赖ISymbol.ContainingAssembly属性。
内容的提问来源于stack exchange,提问作者mark
相关产品推荐
相关产品推荐

