在浏览Roslyn时,我将一个小应用程序放在一起,该应用程序应该包含一个trace语句作为Visual Studio解决方案中找到的每个方法中的第一个语句.我的代码是错误的,只是更新第一种方法.
未按预期工作的行标记为“TODO”注释.请指教.
我也欢迎风格建议,以创建更简化/可读的解决方案.
提前致谢.
…
private void TraceBtn_Click(object sender,RoutedEventArgs e) { var myWorkSpace = new MyWorkspace("...Visual Studio 2012\Projects\Tests.sln"); myWorkSpace.InjectTrace(); myWorkSpace.ApplyChanges(); }
…
using System; using System.Linq; using Roslyn.Compilers; using Roslyn.Compilers.CSharp; using Roslyn.Services; namespace InjectTrace { public class MyWorkspace { private string solutionFile; public string SolutionFile { get { return solutionFile; } set { if (string.IsNullOrEmpty(value)) throw new Exception("Invalid Solution File"); solutionFile = value; } } private IWorkspace loadedWorkSpace; public IWorkspace LoadedWorkSpace { get { return loadedWorkSpace; } } public ISolution CurrentSolution { get; private set; } public IProject CurrentProject { get; private set; } public IDocument CurrentDocument { get; private set; } public ISolution NewSolution { get; private set; } public MyWorkspace(string solutionFile) { this.SolutionFile = solutionFile; this.loadedWorkSpace = Workspace.LoadSolution(SolutionFile); } public void InjectTrace() { int projectCtr = 0; int documentsCtr = 0; int transformedMembers = 0; int transformedClasses = 0; this.CurrentSolution = this.LoadedWorkSpace.CurrentSolution; this.NewSolution = this.CurrentSolution; //For Each Project... foreach (var projectId in LoadedWorkSpace.CurrentSolution.ProjectIds) { CurrentProject = NewSolution.GetProject(projectId); //..for each Document in the Project.. foreach (var docId in CurrentProject.DocumentIds) { CurrentDocument = NewSolution.GetDocument(docId); var docRoot = CurrentDocument.GetSyntaxRoot(); var newDocRoot = docRoot; var classes = docRoot.DescendantNodes().OfType<ClassDeclarationSyntax>(); IDocument newDocument = null; //..for each Class in the Document.. foreach (var @class in classes) { var methods = @class.Members.OfType<MethodDeclarationSyntax>(); //..for each Member in the Class.. foreach (var currMethod in methods) { //..insert a Trace Statement var newMethod = InsertTrace(currMethod); transformedMembers++; //TODO: PROBLEM IS HERE newDocRoot = newDocRoot.ReplaceNode(currMethod,newMethod); } if (transformedMembers != 0) { newDocument = CurrentDocument.UpdateSyntaxRoot(newDocRoot); transformedMembers = 0; transformedClasses++; } } if (transformedClasses != 0) { NewSolution = NewSolution.UpdateDocument(newDocument); transformedClasses = 0; } documentsCtr++; } projectCtr++; if (projectCtr > 2) return; } } public MethodDeclarationSyntax InsertTrace(MethodDeclarationSyntax currMethod) { var traceText = @"System.Diagnostics.Trace.WriteLine(""Tracing: '" + currMethod.Ancestors().OfType<NamespaceDeclarationSyntax>().Single().Name + "." + currMethod.Identifier.ValueText + "'\");"; var traceStatement = Syntax.ParseStatement(traceText); var bodyStatementsWithTrace = currMethod.Body.Statements.Insert(0,traceStatement); var newBody = currMethod.Body.Update(Syntax.Token(SyntaxKind.OpenBraceToken),bodyStatementsWithTrace,Syntax.Token(SyntaxKind.CloseBraceToken)); var newMethod = currMethod.ReplaceNode(currMethod.Body,newBody); return newMethod; } public void ApplyChanges() { LoadedWorkSpace.ApplyChanges(CurrentSolution,NewSolution); } }
}
@R_502_323@
您的代码的根本问题是newDocRoot = newDocRoot.ReplaceNode(currMethod,newMethod);以某种方式重建newDocRoot代码的内部表示,以便下一个currMethod元素不会在其中找到,下一个ReplaceNode调用将不执行任何操作.这种情况类似于在其foreach循环中修改集合.
解决方案是收集所有必要的更改并使用ReplaceNodes方法立即应用它们.事实上,这自然会导致代码的简化,因为我们不需要跟踪所有这些计数器.我们只需存储所有需要的转换并立即将它们应用于整个文档.
更改后的工作代码:
public void InjectTrace() { this.CurrentSolution = this.LoadedWorkSpace.CurrentSolution; this.NewSolution = this.CurrentSolution; //For Each Project... foreach (var projectId in LoadedWorkSpace.CurrentSolution.ProjectIds) { CurrentProject = NewSolution.GetProject(projectId); //..for each Document in the Project.. foreach (var docId in CurrentProject.DocumentIds) { var dict = new Dictionary<CommonSyntaxNode,CommonSyntaxNode>(); CurrentDocument = NewSolution.GetDocument(docId); var docRoot = CurrentDocument.GetSyntaxRoot(); var classes = docRoot.DescendantNodes().OfType<ClassDeclarationSyntax>(); //..for each Class in the Document.. foreach (var @class in classes) { var methods = @class.Members.OfType<MethodDeclarationSyntax>(); //..for each Member in the Class.. foreach (var currMethod in methods) { //..insert a Trace Statement dict.Add(currMethod,InsertTrace(currMethod)); } } if (dict.Any()) { var newDocRoot = docRoot.ReplaceNodes(dict.Keys,(n1,n2) => dict[n1]); var newDocument = CurrentDocument.UpdateSyntaxRoot(newDocRoot); NewSolution = NewSolution.UpdateDocument(newDocument); } } } }