问题
I would like to build up a new IDocument object step by step using the following class as a specific example. You can start with any object you like and use any intermediate objects you like as long as the resulting object is an IDocument which represents the complete class at the end.
Step #1: Add a new namespace called MyNamespace. Printing out the current object should look like this at this point:
namespace MyNamespace
{
}
Step #2: Add a new class to this namespace called MyClass. Printing out the current object should look like this at this point:
namespace MyNamespace
{
public class MyClass
{
}
}
Step #3: Add a new method to this class called MyMethod. Printing out the current object should look like this at this point:
namespace MyNamespace
{
public class MyClass
{
public void MyMethod()
{
}
}
}
The problem I am having with this is there seems to be a gazillion ways you could theoretically go about this, or at least incorrectly conclude you could go about this. Endless methods and constructors in all kinds of different objects like WithChanges, UpdateDocument, methods on the various Syntax objects, ParseCompilationUnit, etc.
Basically, I want to build this up in an incremental fashion with a distinct object at each step that I could print out to the console for example, not one big statement that creates this whole thing in one line. I have read all the documentation that comes with the June release of the CTP several times, and as I have mentioned I am lost in the seemingly endless combinations of various constructors and methods. Also, I am interested in a way that takes performance into account as well.
回答1:
To build everything up piecemeal as you suggest I would write something like the code below. I would also encourage you to take a look at the ImplementINotifyPropertyChanged sample, as it does a fair amout of syntax construction and re-writing. Note, that as you suggest, there are a variety of ways that you could do this. That's because the API is designed to support scenarios such as editors, so you could build this by applying text changes for each keystroke of a user typing as well. Which API is the right one depends on what you are trying to achieve.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
using Roslyn.Services.CSharp;
class Program
{
static void Main(string[] args)
{
// Create the solution with an empty document
ProjectId projectId;
DocumentId documentId;
var solution = Solution.Create(SolutionId.CreateNewId())
.AddProject("MyProject", "MyProject", LanguageNames.CSharp, out projectId)
.AddDocument(projectId, @"C:\file.cs", string.Empty, out documentId);
var document = solution.GetDocument(documentId);
var root = (CompilationUnitSyntax)document.GetSyntaxRoot();
// Add the namespace
var namespaceAnnotation = new SyntaxAnnotation();
root = root.WithMembers(
Syntax.NamespaceDeclaration(
Syntax.ParseName("MyNamespace"))
.NormalizeWhitespace()
.WithAdditionalAnnotations(namespaceAnnotation));
document = document.UpdateSyntaxRoot(root);
Console.WriteLine("-------------------");
Console.WriteLine("With Namespace");
Console.WriteLine(document.GetText().GetText());
// Find our namespace, add a class to it, and update the document
var namespaceNode = (NamespaceDeclarationSyntax)root
.GetAnnotatedNodesAndTokens(namespaceAnnotation)
.Single()
.AsNode();
var classAnnotation = new SyntaxAnnotation();
var newNamespaceNode = namespaceNode
.WithMembers(
Syntax.List<MemberDeclarationSyntax>(
Syntax.ClassDeclaration("MyClass")
.WithAdditionalAnnotations(classAnnotation)));
root = root.ReplaceNode(namespaceNode, newNamespaceNode).NormalizeWhitespace();
document = document.UpdateSyntaxRoot(root);
Console.WriteLine("-------------------");
Console.WriteLine("With Class");
Console.WriteLine(document.GetText().GetText());
// Find the class, add a method to it and update the document
var classNode = (ClassDeclarationSyntax)root
.GetAnnotatedNodesAndTokens(classAnnotation)
.Single()
.AsNode();
var newClassNode = classNode
.WithMembers(
Syntax.List<MemberDeclarationSyntax>(
Syntax.MethodDeclaration(
Syntax.ParseTypeName("void"),
"MyMethod")
.WithBody(
Syntax.Block())));
root = root.ReplaceNode(classNode, newClassNode).NormalizeWhitespace();
document = document.UpdateSyntaxRoot(root);
Console.WriteLine("-------------------");
Console.WriteLine("With Method");
Console.WriteLine(document.GetText().GetText());
}
}
来源:https://stackoverflow.com/questions/11174593/whats-the-most-efficient-way-to-build-up-a-idocument-from-the-very-start