C#通过Roslyn获取代码中的引用及签名
Roslyn可以对代码进行分析,查找代码并替换代码。参考文档如下:
https://roslynquoter.azurewebsites.net/
https://devblogs.microsoft.com/visualstudio/roslyn-syntax-visualizers/
https://jonskeet.uk/csharp/index.html
https://joshvarty.com/2014/07/06/learn-roslyn-now-part-2-analyzing-syntax-trees-with-linq/
https://joshvarty.com/learn-roslyn-now/
有这么一个需求:找到代码中所有的引用并打印出来。
如果是手动查找的话,容易出错且工作量比较大,因此通过Roslyn可以自动地将代码的信息提取出来。步骤如下:
1.DemoApp,一个简单的函数,有四个地方引用这个函数,现在需要做的是通过Roslyn查找四个引用
class Program { /// <summary> /// 方法入口123 /// </summary> /// <param name="args"></param> static void Main(string[] args) { var c1 = Plus(1, 2); var c2 = Plus(3, 4); var c3 = Plus(5,55); var c4 = Plus(6, 666); } static int Plus(int a,int b) { return a + b; } }
2.初始化Roslyn
初始化包含: MSBuildLocator.RegisterDefaults(); MSBuildWorkspace.Create(); workspace.OpenSolutionAsync() 等方法。
Init(); var workspace = MSBuildWorkspace.Create(); workspace.SkipUnrecognizedProjects = true; workspace.LoadMetadataForReferencedProjects = false; workspace.WorkspaceFailed += Workspace_WorkspaceFailed; var solution = workspace.OpenSolutionAsync(@"D:\测试代码\2022-06-27-DemoApp\2022-06-27-DemoApp.sln").Result;
3.找到引用:
var machineMgrProj = solution.Projects.FirstOrDefault(x => x.Name == projectName); var sysLogDoc = machineMgrProj.Documents.FirstOrDefault(x => x.Name == csFileName); var semanticModel2 = sysLogDoc.GetSemanticModelAsync().Result; var rootNode2 = sysLogDoc.GetSyntaxRootAsync().Result; var nodeClass = rootNode2.DescendantNodes().OfType<ClassDeclarationSyntax>() .FirstOrDefault(x => x.Identifier.ToString() == className1); var nodeMethod = nodeClass.DescendantNodes() .OfType<MethodDeclarationSyntax>().FirstOrDefault(x => x.Identifier.ToString() == methodName && x.ParameterList.ToString() == methodParListString); var sampleMethodSymbol1 = semanticModel2.GetDeclaredSymbol(nodeMethod); var referencesToSampleMethod2 = SymbolFinder.FindReferencesAsync(sampleMethodSymbol1, solution).Result;
4.解析引用:
//文件名 var fileName = location.Document.Name; //syntex tree var syntaxTree = location.Location.SourceTree; var root = syntaxTree.GetRoot(); //class node var cc1c = root.DescendantNodes().OfType<ClassDeclarationSyntax>().First(); //类名 var className = cc1c.Identifier.ValueText; if (!fileName.Contains(".cs")) { } //location node-inovation node var nodeFind = syntaxTree.GetRoot().FindNode(location.Location.SourceSpan); //method node var parentMethod = GetParentMethod<MethodDeclarationSyntax>(nodeFind); //方法名 var methodName1 = parentMethod.Identifier.ValueText; //方法注释 //method token var t1 = parentMethod.DescendantTokens(); var t2 = t1 .Where(x => x.IsKind(SyntaxKind.PrivateKeyword) || x.IsKind(SyntaxKind.PublicKeyword) || x.IsKind(SyntaxKind.ProtectedKeyword)||x.IsKind(SyntaxKind.StaticKeyword)).FirstOrDefault(); //token的细节信息 var trivia = t2.LeadingTrivia.Where(x => x.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) || x.IsKind(SyntaxKind.MultiLineCommentTrivia)) .FirstOrDefault(); var comment = GetComment(trivia); //参数 var invocation = GetParentMethod<InvocationExpressionSyntax>(nodeFind); var parameter = invocation.DescendantNodes().OfType<ArgumentListSyntax>() .Where(x => x.Span.Start >= nodeFind.Span.End).First(); var argumentss = parameter.DescendantNodes().OfType<ArgumentSyntax>().ToList();
最后,效果如下图,查找到了4个引用,及引用的参数值
源码下载:https://files.cnblogs.com/files/congqiandehoulai/%E6%B5%8B%E8%AF%95N%E6%96%B9%E6%B3%95%E6%AC%A1%E6%95%B0_2.rar
https://files.cnblogs.com/files/congqiandehoulai/2022-06-27-DemoApp.rar