如何以编程方式获取 class 依赖项及其各自的文件位置?
How can I get class dependencies programmatically and their respective file locations?
我需要在给定项目的 class 之间获得某种依赖关系图,即特定 class 使用的所有 class 之间。我想知道给定的 class 使用了哪些 classes,以便我以后可以在项目中找到它们的文件路径。考虑以下简单示例:
public class Dog: Animal, IBark
{
public void Bark()
{
// Code to bark.
}
public void Fight(Cat cat)
{
// Code to fight cat.
}
}
对于这个具体示例,我想知道 Dog
class 使用哪个 classes。所以我想以编程方式访问具有这些依赖项的对象。在这种情况下,该对象将包含 IBark
、Animal
和 Cat
classes/interfaces 以及它们各自的文件路径。
这在 C# 中可行吗?我已经尝试查看 Roslyn API,虽然我可以解析文档并遍历它以找到节点,但我还没有找到一种方法来获取与这些节点相关的元数据,这些元数据可能会给我我正在寻找的东西(例如文件路径)。这让我想知道是否有更好的方法来解决这个问题。
这可以通过 Roslyn
api 来完成。算法如下:
- 加载解决方案 (.sln)
- 遍历项目 (.csproj)
- 遍历项目中的文档 (.cs)
- 为文档加载
semantic model
- 得到
SyntaxTree
并遍历所有SyntaxNode
- 检测每个
SyntaxNode
具体类型。如果检测到的语法是 class 定义(比如说 Dog)——继续遍历,考虑进一步检测到 class 或接口依赖于 Dog
示例代码如下。我也在 github. You will be interested in sample unit tests and sample solution based on your example 中犯了这个错误。我假设单个文件仅包含一个 class 定义,但我认为这应该足以让您开始。
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);
}
}
对于上面的代码,工作区是按以下方式加载的:
var workspace = MSBuildWorkspace.Create();
await workspace.OpenSolutionAsync(solutionFilePath);
我需要在给定项目的 class 之间获得某种依赖关系图,即特定 class 使用的所有 class 之间。我想知道给定的 class 使用了哪些 classes,以便我以后可以在项目中找到它们的文件路径。考虑以下简单示例:
public class Dog: Animal, IBark
{
public void Bark()
{
// Code to bark.
}
public void Fight(Cat cat)
{
// Code to fight cat.
}
}
对于这个具体示例,我想知道 Dog
class 使用哪个 classes。所以我想以编程方式访问具有这些依赖项的对象。在这种情况下,该对象将包含 IBark
、Animal
和 Cat
classes/interfaces 以及它们各自的文件路径。
这在 C# 中可行吗?我已经尝试查看 Roslyn API,虽然我可以解析文档并遍历它以找到节点,但我还没有找到一种方法来获取与这些节点相关的元数据,这些元数据可能会给我我正在寻找的东西(例如文件路径)。这让我想知道是否有更好的方法来解决这个问题。
这可以通过 Roslyn
api 来完成。算法如下:
- 加载解决方案 (.sln)
- 遍历项目 (.csproj)
- 遍历项目中的文档 (.cs)
- 为文档加载
semantic model
- 得到
SyntaxTree
并遍历所有SyntaxNode
- 检测每个
SyntaxNode
具体类型。如果检测到的语法是 class 定义(比如说 Dog)——继续遍历,考虑进一步检测到 class 或接口依赖于 Dog
示例代码如下。我也在 github. You will be interested in sample unit tests and sample solution based on your example 中犯了这个错误。我假设单个文件仅包含一个 class 定义,但我认为这应该足以让您开始。
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);
}
}
对于上面的代码,工作区是按以下方式加载的:
var workspace = MSBuildWorkspace.Create();
await workspace.OpenSolutionAsync(solutionFilePath);