使用 Roslyn 排序构造函数参数
Order constructor parameters with Roslyn
我正在尝试创建对构造函数参数进行排序的方法。它导航良好,甚至更新树,但序列化文本包含原始参数:
static void Transform(string sourceCode)
{
var tree = CSharpSyntaxTree.ParseText(sourceCode);
var root = (CompilationUnitSyntax)tree.GetRoot();
var @namespace = (NamespaceDeclarationSyntax)
root.ChildNodes().First(n => n.Kind() == SyntaxKind.NamespaceDeclaration);
var @class = (ClassDeclarationSyntax)
@namespace.ChildNodes().First(n => n.Kind() == SyntaxKind.ClassDeclaration);
var constructor = (ConstructorDeclarationSyntax)
@class.ChildNodes().First(n => n.Kind() == SyntaxKind.ConstructorDeclaration);
var parameters = constructor.ParameterList
.ChildNodes()
.Cast<ParameterSyntax>()
.OrderBy(node => ((IdentifierNameSyntax) node.Type).Identifier.ToString())
.Select(node => SyntaxFactory.Parameter(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(),
SyntaxFactory.ParseTypeName(((IdentifierNameSyntax)node.Type).Identifier.Text),
SyntaxFactory.Identifier(node.Identifier.Text),
null));
var updatedParameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters));
((SyntaxNode)constructor).ReplaceNode(constructor.ParameterList, updatedParameterList);
Console.WriteLine(root.GetText().ToString());
}
我这样称呼它:
Transform(@"
namespace Test {
class Test {
Test(IParam2 param2, IParam1 param1) { }
}
}");
并期望
namespace Test {
class Test {
Test(IParam1 param1, IParam2 param2) { }
}
}
但输出的参数顺序仍然错误。有什么想法吗?
问题是 Roslyn 中的所有树都是不可变的。
令人惊讶的是,这一行实际上并没有做任何事情:
((SyntaxNode)constructor).ReplaceNode(constructor.ParameterList, updatedParameterList);
那是因为 ReplaceNode()
returns 一个全新的语法树,并没有操纵旧的语法树。
这就是您要查找的内容:
static void TransformParameterOrder(string sourceCode)
{
var tree = CSharpSyntaxTree.ParseText(sourceCode);
var root = (CompilationUnitSyntax)tree.GetRoot();
var @namespace = (NamespaceDeclarationSyntax)
root.ChildNodes().First(n => n.Kind() == SyntaxKind.NamespaceDeclaration);
var @class = (ClassDeclarationSyntax)
@namespace.ChildNodes().First(n => n.Kind() == SyntaxKind.ClassDeclaration);
var constructor = (ConstructorDeclarationSyntax)
@class.ChildNodes().First(n => n.Kind() == SyntaxKind.ConstructorDeclaration);
var child = constructor.ParameterList.ChildNodes().Count();
var parameters = constructor.ParameterList
.ChildNodes()
.Cast<ParameterSyntax>()
.OrderBy(node => ((IdentifierNameSyntax)node.Type).Identifier.ToString())
.Select(node => SyntaxFactory.Parameter(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(),
SyntaxFactory.ParseTypeName(((IdentifierNameSyntax)node.Type).Identifier.Text),
SyntaxFactory.Identifier(node.Identifier.Text),
null))
.Take(2);
var updatedParameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters));
var newNode = ((SyntaxNode)constructor).ReplaceNode(constructor.ParameterList, updatedParameterList);
//Alternatively you can assign root = root.ReplaceNode...
var newRoot = root.ReplaceNode(constructor.ParameterList, updatedParameterList);
Console.WriteLine(root.GetText().ToString());
Console.WriteLine(newRoot.GetText().ToString());
}
我正在尝试创建对构造函数参数进行排序的方法。它导航良好,甚至更新树,但序列化文本包含原始参数:
static void Transform(string sourceCode)
{
var tree = CSharpSyntaxTree.ParseText(sourceCode);
var root = (CompilationUnitSyntax)tree.GetRoot();
var @namespace = (NamespaceDeclarationSyntax)
root.ChildNodes().First(n => n.Kind() == SyntaxKind.NamespaceDeclaration);
var @class = (ClassDeclarationSyntax)
@namespace.ChildNodes().First(n => n.Kind() == SyntaxKind.ClassDeclaration);
var constructor = (ConstructorDeclarationSyntax)
@class.ChildNodes().First(n => n.Kind() == SyntaxKind.ConstructorDeclaration);
var parameters = constructor.ParameterList
.ChildNodes()
.Cast<ParameterSyntax>()
.OrderBy(node => ((IdentifierNameSyntax) node.Type).Identifier.ToString())
.Select(node => SyntaxFactory.Parameter(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(),
SyntaxFactory.ParseTypeName(((IdentifierNameSyntax)node.Type).Identifier.Text),
SyntaxFactory.Identifier(node.Identifier.Text),
null));
var updatedParameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters));
((SyntaxNode)constructor).ReplaceNode(constructor.ParameterList, updatedParameterList);
Console.WriteLine(root.GetText().ToString());
}
我这样称呼它:
Transform(@"
namespace Test {
class Test {
Test(IParam2 param2, IParam1 param1) { }
}
}");
并期望
namespace Test {
class Test {
Test(IParam1 param1, IParam2 param2) { }
}
}
但输出的参数顺序仍然错误。有什么想法吗?
问题是 Roslyn 中的所有树都是不可变的。
令人惊讶的是,这一行实际上并没有做任何事情:
((SyntaxNode)constructor).ReplaceNode(constructor.ParameterList, updatedParameterList);
那是因为 ReplaceNode()
returns 一个全新的语法树,并没有操纵旧的语法树。
这就是您要查找的内容:
static void TransformParameterOrder(string sourceCode)
{
var tree = CSharpSyntaxTree.ParseText(sourceCode);
var root = (CompilationUnitSyntax)tree.GetRoot();
var @namespace = (NamespaceDeclarationSyntax)
root.ChildNodes().First(n => n.Kind() == SyntaxKind.NamespaceDeclaration);
var @class = (ClassDeclarationSyntax)
@namespace.ChildNodes().First(n => n.Kind() == SyntaxKind.ClassDeclaration);
var constructor = (ConstructorDeclarationSyntax)
@class.ChildNodes().First(n => n.Kind() == SyntaxKind.ConstructorDeclaration);
var child = constructor.ParameterList.ChildNodes().Count();
var parameters = constructor.ParameterList
.ChildNodes()
.Cast<ParameterSyntax>()
.OrderBy(node => ((IdentifierNameSyntax)node.Type).Identifier.ToString())
.Select(node => SyntaxFactory.Parameter(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(),
SyntaxFactory.ParseTypeName(((IdentifierNameSyntax)node.Type).Identifier.Text),
SyntaxFactory.Identifier(node.Identifier.Text),
null))
.Take(2);
var updatedParameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters));
var newNode = ((SyntaxNode)constructor).ReplaceNode(constructor.ParameterList, updatedParameterList);
//Alternatively you can assign root = root.ReplaceNode...
var newRoot = root.ReplaceNode(constructor.ParameterList, updatedParameterList);
Console.WriteLine(root.GetText().ToString());
Console.WriteLine(newRoot.GetText().ToString());
}