2013-11-22 2 views
6

Roslyn을 탐색하면서 Visual Studio Solution의 모든 메서드에서 첫 번째 문으로 trace 문을 포함시켜야하는 작은 응용 프로그램을 만들었습니다. 내 코드는 버그가 있으며 첫 번째 메소드 만 업데이트하고있다.Roslyn을 사용하는 메서드 노드 바꾸기

예상대로 작동하지 않는 줄에는 "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); 
    } 


} 

}

답변

6

당신 코드의 근본 문제는 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); 
      } 
     } 
    } 
} 
관련 문제