使用語法樹替換 C 中所有方法的現有屬性

以下程式碼段替換了名為 PreviousAttribute 的所有屬性,名為 ReplacementAttribute,用於整個解決方案。該示例手動搜尋語法樹並替換所有受影響的節點。

    static async Task<bool> ModifySolution(string solutionPath)
    {
        using (var workspace = MSBuildWorkspace.Create())
        {
            // Selects a Solution File
            var solution = await workspace.OpenSolutionAsync(solutionPath);
            // Iterates through every project
            foreach (var project in solution.Projects)
            {
                // Iterates through every file
                foreach (var document in project.Documents)
                {
                    // Selects the syntax tree
                    var syntaxTree = await document.GetSyntaxTreeAsync();
                    var root = syntaxTree.GetRoot();
                    // Finds all Attribute Declarations in the Document
                    var existingAttributesList = root.DescendantNodes().OfType<AttributeListSyntax>()
                        // Where the Attribute is declared on a method
                        .Where(curr => curr.Parent is MethodDeclarationSyntax)
                        // And the attribute is named "PreviousAttribute"
                        .Where(curr => curr.Attributes.Any(currentAttribute => currentAttribute.Name.GetText().ToString() == "PreviousAttribute"))
                        .ToList();
                    if (existingAttributesList.Any())
                    {
                        // Generates a replacement for every attribute
                        var replacementAttribute = SyntaxFactory.AttributeList(
                            SyntaxFactory.SingletonSeparatedList(
                            SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("ReplacementAttribute"),
                                SyntaxFactory.AttributeArgumentList(
                                    SyntaxFactory.SeparatedList(new[]
                                    {
                                    SyntaxFactory.AttributeArgument(
                                        SyntaxFactory.LiteralExpression(
                                            SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(@"Sample"))
                                        )
                                    })))));
                        // Replaces all attributes at once.
                        // Note that you should not use root.ReplaceNode
                        // since it would only replace the first note
                        root = root.ReplaceNodes(existingAttributesList, (node, n2) => replacementAttribute);
                        // Exchanges the document in the solution by the newly generated document
                        solution = solution.WithDocumentSyntaxRoot(document.Id, root);
                    }
                }
            }
            // applies the changes to the solution
            var result = workspace.TryApplyChanges(solution);
            return result;
        }
    }

可以針對以下類測試上面的示例:

public class Program
{
    [PreviousAttribute()]
    static void Main(string[] args)
    {
    }
}

你不應該使用 Methode root.ReplaceNode 來替換多個節點。由於樹是不可變的,因此你將處理不同的物件。在上面的示例中使用以下程式碼段不會產生預期的結果:

foreach(var node in existingAttributesList){
    root = root.ReplaceNode(node, replacementAttribute);
}

第一次呼叫 ReplaceNode 會建立一個新的根元素。然而,existingAttributesList 中的元素屬於不同的根(前一個根元素),因此無法替換。這將導致第一個屬性被替換,並且以下屬性保持不變,因為所有連續呼叫都將在新樹中不存在的節點上執行。