在Roslyn中如何判断来自不同Compilation的两个符号是否等价?
跨Compilation符号等价性判断方案
跨Compilation生成的ISymbol实例本身是相互独立的,SymbolEqualityComparer.Default仅支持同一Compilation内的符号比较,所以你测试中得到false是Roslyn的正常设计。针对不同SyntaxTree、但源代码完全一致的场景,可根据需求选择以下两种方案,都优于你提到的全量SourceText对比方案:
方案1:语义级等价判断(推荐绝大多数场景使用)
该方案无需对比源代码,基于符号本身的语义属性判断,性能最优,且支持源代码排版(空格、换行)不同但语义完全等价的场景:
- 第一步:校验两个符号的类型是否一致,比如都是类、都是方法、都是属性
- 第二步:校验符号的核心修饰符是否匹配,比如可访问性、是否静态、是否抽象等
- 第三步:用Roslyn内置的全限定名格式化方法对比符号的唯一语义标识
代码示例如下:
using Microsoft.CodeAnalysis; public static bool AreSymbolsSemanticallyEquivalent(ISymbol symbol1, ISymbol symbol2) { // 快速过滤不同类型的符号 if (symbol1.Kind != symbol2.Kind) return false; // 按需添加修饰符校验规则,比如可访问性、是否静态等 if (symbol1.DeclaredAccessibility != symbol2.DeclaredAccessibility) return false; if (symbol1.IsStatic != symbol2.IsStatic) return false; // 获取包含命名空间、类型名、泛型参数等信息的全限定名 string fqn1 = symbol1.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); string fqn2 = symbol2.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); return fqn1 == fqn2; }
如果对比的是方法、属性等成员符号,还可以追加对比参数列表、返回值类型等特征,进一步降低误判概率。
方案2:源代码严格等价判断
如果你的需求要求源代码逐字符完全一致,语义等价但排版不同的情况也算不等,可以优化你原来的思路,只对比符号声明对应的部分源代码,避免全量SourceText对比的性能开销:
using Microsoft.CodeAnalysis; using System.Linq; public static bool AreSymbolsSourceStrictlyEquivalent(ISymbol symbol1, ISymbol symbol2) { var declRef1 = symbol1.DeclaringSyntaxReferences.FirstOrDefault(); var declRef2 = symbol2.DeclaringSyntaxReferences.FirstOrDefault(); // 无声明引用的符号(比如内置类型)直接返回false if (declRef1 == null || declRef2 == null) return false; // 快速校验声明跨度、源代码总长度,过滤明显不同的情况 if (declRef1.Span != declRef2.Span) return false; if (declRef1.SyntaxTree.Length != declRef2.SyntaxTree.Length) return false; // 仅对比声明对应的子文本,无需对比全量源代码 string declText1 = declRef1.SyntaxTree.GetText().GetSubText(declRef1.Span).ToString(); string declText2 = declRef2.SyntaxTree.GetText().GetSubText(declRef2.Span).ToString(); return declText1 == declText2; }
内容的提问来源于stack exchange,提问作者mark
相关产品推荐
相关产品推荐

