You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 14:45:24