Roslyn SyntaxTree — изменение значения поля

#c# #abstract-syntax-tree #roslyn

#c# #аннотация-синтаксическое дерево #roslyn

Вопрос:

используя Roslyn SyntaxTree API, я хотел бы заменить буквальное значение «UV254» новым значением. Пример:

 public class Analog
{
   public const string Value = "UV254";
}
 

После обновления

 public class Analog
{
   public const string Value = "UV220";
}
 

Я придумал приведенное ниже решение, но я подозреваю, что это можно упростить:

 string sourceCode = "public class Analog { public const string Value = "UV254"; public const string Description = "A Description";}";

SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
CompilationUnitSyntax syntaxRoot = syntaxTree.GetCompilationUnitRoot();

LiteralExpressionSyntax afterLiteralExpressionSyntax = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("UV220"));

LiteralExpressionSyntax beforeLiteralExpressionSyntax = null;
foreach (VariableDeclarationSyntax variableDeclarationSyntax in syntaxRoot.DescendantNodes().OfType<VariableDeclarationSyntax>())
{
    foreach(VariableDeclaratorSyntax variableDeclaratorSyntax in variableDeclarationSyntax.Variables)
    {
        if(variableDeclaratorSyntax.Identifier.ValueText == "Value")
        {
            beforeLiteralExpressionSyntax = variableDeclaratorSyntax.DescendantNodes().OfType<LiteralExpressionSyntax>().Single();
            break;
        }
    }
    if(beforeLiteralExpressionSyntax != null)
    {
        break;
    }
}
var newRoot = syntaxRoot.ReplaceNode(beforeLiteralExpressionSyntax, afterLiteralExpressionSyntax);
var fixedTree = newRoot.SyntaxTree.WithRootAndOptions(newRoot, syntaxTree.Options);
 

Можно ли это упростить?
Спасибо за помощь.

Ответ №1:

Я думаю, вы можете использовать некоторый LINQ, чтобы сократить определение beforeLiteralExpressionSyntax . Вместо этого вы могли бы написать следующее:

             LiteralExpressionSyntax beforeLiteralExpressionSyntax =
                syntaxRoot.DescendantNodes().OfType<VariableDeclarationSyntax>()
                .SelectMany(decl => decl.Variables)
                .FirstOrDefault(declarator => declarator.Identifier.ValueText == "Value")
                ?.DescendantNodes().OfType<LiteralExpressionSyntax>()
                .Single();
 

Это присвоит null значение beforeLiteralExpressionSyntax , если поле не было найдено. Если вы уверены, что поле всегда будет там, вы можете заменить FirstOrDefault на First и заменить ?. на . .

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

Комментарии:

1. Спасибо, мне нравится!