使用語法樹替換 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
中的元素屬於不同的根(前一個根元素),因此無法替換。這將導致第一個屬性被替換,並且以下屬性保持不變,因為所有連續呼叫都將在新樹中不存在的節點上執行。