【问题标题】:How to compare type symbols (ITypeSymbol) from different projects in Roslyn?如何比较 Roslyn 中不同项目的类型符号(ITypeSymbol)?
【发布时间】:2015-11-12 21:13:54
【问题描述】:

我在解决方案中有 2 个测试项目。
第一个项目(“VDoc”)声明 VDocQuery 类型。
第二个项目(“VDocQueryTest”)调用 VDocQuery 构造函数。
我得到 2 个 VDocQuery 的 ITypeSymbol(每个项目一个),比​​较它,但得到错误的结果。

步骤:
1. 获取第一个 ITypeSymbol(来自 VDoc 项目,使用 SemanticModel.LookupNamespacesAndTypes() 方法)。
2. 从 VDocQueryTest 项目中获取第二个 ITypeSymbol(来自 ObjectCreationExpressionSyntax.GetTypeInfo().Type)
3.与ITypeSymbol.Equals(ITypeSymbol)比较。

我期待真实的结果,但得到了错误的结果。

问题: 如何在一个解决方案中正确比较来自不同项目的 ITypeSymbol?

代码示例:

class Program
{
    static void Main(string[] args)
    {
        string solutionPath = @"..\..\..\StaticAnalysis.sln";

        MSBuildWorkspace workspace = MSBuildWorkspace.Create();
        Solution solution = workspace.OpenSolutionAsync(solutionPath).Result;

        var vdocProject = FindProjectByName("VDoc", solution);
        SemanticModel semanticModel = vdocProject.Documents.First().GetSemanticModelAsync().Result;
        var nsVDocQueryFunctionalTest = (INamespaceOrTypeSymbol)semanticModel.LookupNamespacesAndTypes(0, null, "VDocQueryFunctionalTest").First();
        var tVDocQuery = (ITypeSymbol)semanticModel.LookupNamespacesAndTypes(0, nsVDocQueryFunctionalTest, "VDocQuery").First();

        TypeInfo ti = GetFromVDocRef(solution);
        bool result1 = ti.Type.Equals(tVDocQuery); // false, expected = true?

        //these 2 lines added after Jason Malinowski answer
        var sVDocQuerySource = SymbolFinder.FindSourceDefinitionAsync(ti.Type, solution).Result;
        bool result2 = sVDocQuerySource.Equals(tVDocQuery); // false, expected = true?

       //this line solved the problem, thanks to @Tamas
       bool result3 = ti.Type.DeclaringSyntaxReferences.FirstOrDefault()?.Equals(tVDocQuery.DeclaringSyntaxReferences.FirstOrDefault()) ?? false;
    }

    private static TypeInfo GetFromVDocRef(Solution solution)
    {
        var vdocQueryTestProject = FindProjectByName("VDocQueryTest", solution);
        var vdocQueryTestProjectSemanticModel = vdocQueryTestProject.Documents.First().GetSemanticModelAsync().Result;
        var compilationUnit = (CompilationUnitSyntax)vdocQueryTestProject.Documents.First().GetSyntaxRootAsync().Result;
        var ns = (NamespaceDeclarationSyntax)compilationUnit.Members[0];
        var cls = (ClassDeclarationSyntax)ns.Members[0];
        var method = (MethodDeclarationSyntax)cls.Members[0];
        var stat = (ExpressionStatementSyntax)method.Body.Statements[0];
        var newExpr = (ObjectCreationExpressionSyntax)stat.Expression;
        var ti = vdocQueryTestProjectSemanticModel.GetTypeInfo(newExpr);
        return ti;
    }

    static Project FindProjectByName(string projectName, Solution solution)
    {
        var project = solution.Projects.SingleOrDefault(p => p.Name == projectName);
        return project;
    }
}

VDocQuery.cs:

using System.Collections.Generic;

namespace VDocQueryFunctionalTest
{
    public class VDocQuery
    {
        public VDocQuery()
        {
        }

        public void AddFields(string docType, params string[] fields)
        {
        }

        public List<VDoc> Execute()
        {
            return null;
        }
    }
}

VDocQueryUse.cs:

using VDocQueryFunctionalTest;

namespace VDocQueryTest
{
    static class VDocQueryUse
    {
        public static void VDocQueryUseTest()
        {
            new VDocQuery();
        }
    }
}

【问题讨论】:

  • 我刚刚尝试了您的原始解决方案和@Jason 的修改,它们都可以正常工作。在这两种情况下,类型都是等价的。您使用的是哪个版本的 Roslyn?您能以某种方式分享您的项目设置和参考资料吗?
  • @Tamas,VS2015 社区版 + Microsoft.Code.Analysis 1.0.0.0(使用 nuget 获取)。我可以发送最小的解决方案。首先,我尝试从零开始创建最小解决方案,但无法重现该问题。所以,在第二次尝试时,我复制了真正的解决方案并删除了所有不必要的代码 - 问题被重现了。

标签: c# roslyn roslyn-code-analysis


【解决方案1】:

我可以通过在两个项目中使用不同的目标框架来重现您的问题。为两个项目设置相同的目标框架,然后您的原始解决方案将找到匹配的类型。

如果你不能改变目标框架,那么你仍然可以比较ITypeSymbol.DeclaringSyntaxReferences

【讨论】:

  • 框架相同 - .NET Framework 4.5.2。以前的框架是 4.5。但我在运行测试前几天改变了它。与 ISymbol.DeclaringSyntaxReferences 的比较是有效的!谢谢。
【解决方案2】:

当您在 VDocQueryTest 项目中引用类型时,获取 ISymbol 并将其传递给 SymbolFinder.FindSourceDefinitionAsync()。这将意识到它是来自其他项目的元数据的符号,然后将 that 项目中的元数据符号交给您。从那里,您可以执行 .Equals()。

【讨论】:

  • 我在示例中添加了 2 行代码,结果相同 - false :( var sVDocQuerySource = SymbolFinder.FindSourceDefinitionAsync(ti.Type, solution).Result; bool result2 = sVDocQuerySource.Equals(tVDocQuery) ;
  • 这很棒。我没有成功尝试将符号 BaseSymbol(除了命名)与我遇到此问题之前的一组符号匹配。
猜你喜欢
  • 1970-01-01
  • 2017-09-07
  • 1970-01-01
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多