如何以编程方式获取类依赖项及其各自的文件位置?

Joã*_*iva 5 c# roslyn .net-core roslyn-code-analysis

我需要获得给定项目的类之间的某种依赖关系图,即该特定类使用的所有类。我想知道给定的类正在使用哪些类,以便以后可以在项目中找到它们的文件路径。考虑以下简单示例:

public class Dog: Animal, IBark
{
    public void Bark()
    {
        // Code to bark.
    }

    public void Fight(Cat cat)
    {
        // Code to fight cat.
    }
}
Run Code Online (Sandbox Code Playgroud)

对于这个具体的例子,我想知道该类Dog使用了哪些类。所以我想以编程方式访问具有这些依赖项的对象。在这种情况下,该对象将包含IBarkAnimalCat类/接口以及可能它们各自的文件路径。

这在 C# 中可能吗?我尝试研究 Roslyn API,虽然我可以解析文档并遍历它来查找节点,但我还没有找到一种方法来获取与这些节点相关的元数据,这些元数据可能会给我我正在寻找的内容(例如文件路径) 。这让我想知道是否有更好的方法来解决这个问题。

Art*_*tem 1

这可以通过 api 来完成Roslyn。算法如下:

  1. 加载解决方案 (.sln)
  2. 迭代项目 (.csproj)
  3. 迭代项目内的文档 (.cs)
  4. 加载semantic model文档
  5. 获取SyntaxTree并遍历所有SyntaxNode
  6. 检测每种SyntaxNode具体类型。如果检测到的语法是类定义(假设为 Dog) - 继续遍历,考虑到进一步检测到的类或接口依赖于 Dog

下面的示例代码。我也在github上提交了这个。您将对基于您的示例的示例单元测试和示例解决方案感兴趣。我假设单个文件仅包含一个类定义,但是我认为这应该足以让您开始。

        var dependencies = new Dictionary<string, List<string>>(); 
        //key - class name, value - list of dependent class names

        var project = workspace.CurrentSolution.Projects.First();

        foreach (var document in project.Documents)
        {
            var semanticModel = await document.GetSemanticModelAsync();
            KeyValuePair<string, List<string>>? keyValue = null;

            foreach (var item in semanticModel.SyntaxTree.GetRoot().DescendantNodes())
            {
                switch (item)
                {
                    case ClassDeclarationSyntax classDeclaration:
                    case InterfaceDeclarationSyntax interfaceDeclaration:
                        if (!keyValue.HasValue)
                        {
                            keyValue = new KeyValuePair<string, List<string>>(semanticModel.GetDeclaredSymbol(item).Name, new List<string>());
                        }
                        break;
                    case SimpleBaseTypeSyntax simpleBaseTypeSyntax:
                        keyValue?.Value.Add(simpleBaseTypeSyntax.Type.ToString());
                        break;
                    case ParameterSyntax parameterSyntax:
                        keyValue?.Value.Add(parameterSyntax.Type.ToString());
                        break;
                }
            }

            if (keyValue.HasValue)
            {
                dependencies.Add(keyValue.Value.Key, keyValue.Value.Value);
            }
        }
Run Code Online (Sandbox Code Playgroud)

对于上面的代码,工作区是通过以下方式加载的:

var workspace = MSBuildWorkspace.Create();
await workspace.OpenSolutionAsync(solutionFilePath);
Run Code Online (Sandbox Code Playgroud)