Я хотел бы создать новый объект IDocument шаг за шагом, используя следующий класс в качестве конкретного примера. Вы можете начать с любого объекта, который вам нравится, и использовать любые промежуточные объекты, которые вам нравятся, при условии, что результирующий объект является IDocument, который представляет полный класс в конце.

Шаг №1: Добавьте новое пространство имен с именем MyNamespace. На этом этапе печать текущего объекта должна выглядеть так:

namespace MyNamespace
{
}

Шаг №2: Добавьте в это пространство имен новый класс MyClass. На этом этапе печать текущего объекта должна выглядеть так:

namespace MyNamespace
{
    public class MyClass
    {
    }
}

Шаг № 3: Добавьте в этот класс новый метод MyMethod. На этом этапе печать текущего объекта должна выглядеть так:

namespace MyNamespace
    {
        public class MyClass
        {
            public void MyMethod()
            {
            }
        }
    }

Проблема, с которой я столкнулся, заключается в том, что, кажется, существует множество способов, которыми вы могли бы теоретически сделать это или, по крайней мере, сделать неправильный вывод, что вы могли бы это сделать. Бесконечные методы и конструкторы во всевозможных объектах, таких как WithChanges, UpdateDocument, методы в различных объектах Syntax, ParseCompilationUnit и т. Д.

По сути, я хочу наращивать это постепенно с отдельным объектом на каждом шаге, который я мог бы, например, распечатать на консоли, а не одним большим оператором, который создает все это в одной строке. Я несколько раз читал всю документацию, поставляемую с июньским выпуском CTP, и, как я уже упоминал, теряюсь в, казалось бы, бесконечных комбинациях различных конструкторов и методов. Кроме того, меня интересует способ, который также учитывает производительность.

2
Beaker 24 Июн 2012 в 06:40

1 ответ

Лучший ответ

Чтобы построить все по частям, как вы предлагаете, я бы написал что-то вроде приведенного ниже кода. Я также рекомендую вам взглянуть на образец ImplementINotifyPropertyChanged, так как в нем достаточно много построений и переписываний синтаксиса. Обратите внимание, что, как вы предлагаете, есть несколько способов сделать это. Это потому, что API разработан для поддержки таких сценариев, как редакторы, поэтому вы можете создать его, применяя изменения текста для каждого нажатия клавиши, вводимого пользователем. Какой API правильный, зависит от того, чего вы пытаетесь достичь.

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());
    }
}
5
Kevin Pilch 24 Июн 2012 в 09:10
Вау, я делал это крайне неэффективно. Я проверю образец, который вы упомянули. Спасибо за помощь, я только что многому научился из вашего ответа!
 – 
Beaker
24 Июн 2012 в 14:47
По-видимому, существует еще более эффективный способ создания SyntaxTree с нуля, который и был ответом на этот вопрос: stackoverflow.com/questions/11351977/…
 – 
Beaker
6 Июл 2012 в 21:54
Конечно, но ваш вопрос явно касается промежуточных шагов.
 – 
Kevin Pilch
7 Июл 2012 в 23:55
На вопрос, который я задал, это правильный ответ, я просто указывал на альтернативу для людей, которым не нужны промежуточные шаги.
 – 
Beaker
14 Июл 2012 в 00:01