如何使用Roslyn将类成员添加到指定位置?
解决Roslyn中ClassDeclarationSyntax.AddMembers()添加方法的位置问题
使用ClassDeclarationSyntax.AddMembers()向类中添加方法时,方法会被自动放入类末尾的#if指令块内,无法直接放到#endif之后的位置。以下是具体场景和解决方案:
当前运行代码
var tree = CSharpSyntaxTree.ParseText(@" namespace Test { public class A { #if !SILVERLIGHT public int someField; #endif } }"); var classNode = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().First(); var previousWhiteSpacesToken = SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.None, SyntaxTriviaList.Empty); var method = SyntaxFactory.MethodDeclaration( // SyntaxFactory.PredefinedType( // SyntaxFactory.Token(SyntaxKind.IntKeyword)), "CalculateSize") // .WithModifiers(SyntaxTokenList.Create(previousWhiteSpacesToken) .Add(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) // .WithBody(SyntaxFactory.Block()).NormalizeWhitespace(); var newClassNode = classNode.AddMembers(method).NormalizeWhitespace(); Console.WriteLine(newClassNode.ToString());
当前得到的结果
public class A { #if !SILVERLIGHT public int someField; public int CalculateSize() { } #endif }
不符合期望的可行结果(编译正确但位置不对)
public class A { public int CalculateSize() { } #if !SILVERLIGHT public int someField; #endif }
期望结果
public class A { #if !SILVERLIGHT public int someField; #endif public int CalculateSize() { } }
解决方案
问题根源是AddMembers()会将新成员追加到类成员列表末尾,而原类中#endif是作为最后一个成员的后置trivia存在,导致新方法被包含在条件编译块内。我们需要手动分离#endif相关的trivia,将其移到新方法的前置trivia中,确保方法出现在#endif之后:
var tree = CSharpSyntaxTree.ParseText(@" namespace Test { public class A { #if !SILVERLIGHT public int someField; #endif } }"); var classNode = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().First(); // 创建目标方法 var method = SyntaxFactory.MethodDeclaration( SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)), "CalculateSize") .WithModifiers(SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithBody(SyntaxFactory.Block()) .NormalizeWhitespace(); // 获取原类的最后一个成员(即被#if包裹的字段) var originalMembers = classNode.Members; var lastMember = originalMembers.Last(); // 分离最后一个成员的后置trivia中的#endif指令 var trailingTrivia = lastMember.GetTrailingTrivia(); var endIfIndex = trailingTrivia.IndexOf(t => t.IsKind(SyntaxKind.EndIfDirectiveTrivia)); var endIfTrivia = trailingTrivia.Skip(endIfIndex).ToSyntaxTriviaList(); var newLastMember = lastMember.WithTrailingTrivia(trailingTrivia.Take(endIfIndex).ToSyntaxTriviaList()); // 给新方法添加包含#endif的前置trivia var methodWithTrivia = method.WithLeadingTrivia(endIfTrivia.Add(SyntaxFactory.ElasticCarriageReturnLineFeed)); // 构建新的成员列表并更新类节点 var newMembers = originalMembers.Replace(lastMember, newLastMember).Add(methodWithTrivia); var newClassNode = classNode.WithMembers(newMembers).NormalizeWhitespace(); Console.WriteLine(newClassNode.ToString());
这段代码通过分离#endif trivia并重新分配,确保新方法被放置在#endif之后,最终输出符合期望的代码结构。
内容的提问来源于stack exchange,提问作者Yang
相关产品推荐
相关产品推荐

