使用语法树替换 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
中的元素属于不同的根(前一个根元素),因此无法替换。这将导致第一个属性被替换,并且以下属性保持不变,因为所有连续调用都将在新树中不存在的节点上执行。