С# Roslyn API: вставка инструкций/методов между членами каждого узла

avatar
0x0000000000000
8 августа 2021 в 22:27
480
1
1

Я только что начал заниматься личным проектом и хотел бы иметь возможность использовать Roslyn API для вставки инструкций/методов между членами класса (такие методы/инструкции)

В настоящее время я могу получить различные дочерние узлы, как здесь: Пространство имен> Класс> Поля/Методы

Но я хотел бы знать, как вставить код между полями/методами и не заменять код.

(Например, в месте красных линий ниже)

Nodes

РЕДАКТИРОВАТЬ: после дополнительных исследований я обнаружил, что могу использовать InsertTokensBefore, InsertNodesBefore или InsertTriviaBefore, но я не понимаю, как анализировать мои функции (фактически в текстовом/строковом формате) по нужным параметрам.

Источник

Ответы (1)

avatar
FlashOver
17 августа 2021 в 01:37
3

Вы можете использовать Microsoft.CodeAnalysis.CSharp.SyntaxFactory для создания Microsoft.CodeAnalysis.SyntaxNode

.<75735286>.

Чтобы получить код фабрики синтаксиса, необходимый для создания новых синтаксических деревьев, взгляните на фантастический RoslynQuoter. Там вы можете поместить программу C#, которую хотите создать, с помощью API фабрики синтаксиса:

.
public class RoslynClass
{
    public static int RoslynMethod(int left, int right)
    {
        return left + right;
    }
}

Отметьте все параметры по своему усмотрению и Получите вызовы Roslyn API для создания этого кода!, который вы можете (частично) использовать в своем производственном коде.

Здесь я перечисляю пример A Рефакторинг исходного кода , который вводит метод public static int RoslynMethod(int left, int right) До/после любого A Field <77392065355555555929992923353535298> Field <7739206535355555555929929239235355298>.

>
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;

namespace RoslynTool
{
    [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(MethodInserter))]
    [Shared]
    internal sealed class MethodInserter : CodeRefactoringProvider
    {
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
            SyntaxNode node = root.FindNode(context.Span);

            if (node is FieldDeclarationSyntax or MethodDeclarationSyntax)
            {
                SyntaxNode newNode = CreateMethodNode();

                context.RegisterRefactoring(CodeAction.Create("Insert method before", ct => InsertBeforeAsync(context.Document, node, newNode, ct)));
                context.RegisterRefactoring(CodeAction.Create("Insert method after", ct => InsertAfterAsync(context.Document, node, newNode, ct)));
            }
        }

        private static MemberDeclarationSyntax CreateMethodNode()
        {
            return SyntaxFactory.MethodDeclaration(
                SyntaxFactory.PredefinedType(
                    SyntaxFactory.Token(SyntaxKind.IntKeyword)),
                SyntaxFactory.Identifier("RoslynMethod"))
                .WithModifiers(
                    SyntaxFactory.TokenList(new[] {
                        SyntaxFactory.Token(SyntaxKind.PublicKeyword),
                        SyntaxFactory.Token(SyntaxKind.StaticKeyword)}))
                .WithParameterList(
                    SyntaxFactory.ParameterList(
                        SyntaxFactory.SeparatedList<ParameterSyntax>(new SyntaxNodeOrToken[] {
                            SyntaxFactory.Parameter(
                                SyntaxFactory.Identifier("left"))
                            .WithType(
                                SyntaxFactory.PredefinedType(
                                    SyntaxFactory.Token(SyntaxKind.IntKeyword))),
                            SyntaxFactory.Token(SyntaxKind.CommaToken),
                            SyntaxFactory.Parameter(
                                SyntaxFactory.Identifier("right"))
                            .WithType(
                                SyntaxFactory.PredefinedType(
                                    SyntaxFactory.Token(SyntaxKind.IntKeyword)))})))
                .WithBody(
                    SyntaxFactory.Block(
                        SyntaxFactory.SingletonList<StatementSyntax>(
                            SyntaxFactory.ReturnStatement(
                                SyntaxFactory.BinaryExpression(
                                    SyntaxKind.AddExpression,
                                    SyntaxFactory.IdentifierName("left"),
                                    SyntaxFactory.IdentifierName("right"))))));
        }

        private static async Task<Document> InsertBeforeAsync(Document document, SyntaxNode location, SyntaxNode newNode, CancellationToken cancellationToken)
        {
            DocumentEditor documentEditor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
            documentEditor.InsertBefore(location, newNode);

            return documentEditor.GetChangedDocument();
        }

        private static async Task<Document> InsertAfterAsync(Document document, SyntaxNode location, SyntaxNode newNode, CancellationToken cancellationToken)
        {
            DocumentEditor documentEditor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
            documentEditor.InsertAfter(location, newNode);

            return documentEditor.GetChangedDocument();
        }
    }
}

insert method via roslyn source code refactorings in Visual Studio 2019

Альтернативно, PARSE SyntaxFactory можно использовать для создания нового hald -haldode, чтобы создать новый > haldode, чтобы быть использованным. пример:

namespace RoslynTool
{
    internal sealed class MethodInserter : CodeRefactoringProvider
    {
        private static MemberDeclarationSyntax CreateMethodNode()
        {
            return SyntaxFactory.ParseMemberDeclaration(@"
        public static int RoslynMethod(int left, int right)
        {
            return left + right;
        }
");
        }
    }
}

Надеюсь, я правильно понял ваш вопрос.

0x0000000000000
17 августа 2021 в 12:57
0

Я не уверен, соответствует ли это моим требованиям, так как все нужно делать во время выполнения, я хочу использовать RoselynAPI для преобразования источника ресурсов, который будет создан во время выполнения. Кроме того, поскольку методы, которые нужно вставить, заранее неизвестны, я не могу преобразовать их в C# RoslynMethod. Мне нужно поместить их как «текст» или разобрать из текста. В любом случае спасибо за помощь

FlashOver
17 августа 2021 в 21:39
0

@0x0000000000000 Является ли ресурс, из которого вы хотите сгенерировать код C#, доступным только во время выполнения или уже полностью доступным во время компиляции? Поскольку, если ресурс уже известен во время компиляции и вы используете пакет SDK для .NET 5.0 (или более позднюю версию), то, возможно, генераторы исходного кода могут вам помочь. Предупреждение здесь заключается в том, что вы не можете вставлять какой-либо новый код в существующие файлы, а вместо этого помечаете свой тип как partial, а затем «расширяете» его с помощью генератора исходного кода во время dotnet build.

0x0000000000000
18 августа 2021 в 01:13
0

Поток на самом деле таков: получение исходного кода из встроенного файла ресурсов (.CS), модификация во время выполнения (мы здесь), а затем компиляция кода во время выполнения с использованием поставщика CSharpCode. Ресурс получается в виде строки, это обычный источник CS. Все делается, кроме модификации во время выполнения с помощью Roselyn. Для вставленного кода МЫ не знаем его заранее, но МЫ также получаем его как строку.

0x0000000000000
18 августа 2021 в 13:46
0

Поскольку на самом деле никто другой не предлагает более точного решения моей проблемы, я скоро наградю вас, если никто не ответит.

FlashOver
18 августа 2021 в 21:59
0

@ 0x0000000000000 Боюсь, я не совсем знаком с генерацией кода во время выполнения. Я умею генерировать код во время разработки с помощью исправлений/рефакторингов кода, а также генерировать код во время компиляции с помощью генераторов исходного кода, но у меня очень мало опыта с генерацией кода во время выполнения . Извини.