简体   繁体   中英

Adding class members into specific locations using Roslyn?

I'm using ClassDeclarationSyntax.AddMembers() to add methods to the class. The method appears in the class, but I would like to know how to add the method to a specific place. As of now, they are added to the #if directive at the end of the class.

Roslyn version: 4.4.0

Running the code:

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());

This result will be obtained:

public class A
{
#if !SILVERLIGHT
    public int someField;
    public int CalculateSize()
    {
    }
#endif
}

I know that there is a way to do this, but this result can only guarantee correctness:

public class A
{
    public int CalculateSize()
    {
    }
#if !SILVERLIGHT
    public int someField;
#endif
}

I expected this result:

public class A
{
#if !SILVERLIGHT
    public int someField;
#endif
    public int CalculateSize()
    {
    }  
}

The problem here is that #endif is the leading trivia of } that closes the class declaration. So, when you just add a member, the #endif remains as the leading trivia of } .

What you need to do is to move the leading trivia of } to be the leading trivia of the new method you are adding.

Here is the modified version of your code:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

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())
    .WithLeadingTrivia(classNode.CloseBraceToken.LeadingTrivia).NormalizeWhitespace(); // copy '}' leading trivia to the new method.

// remove leading trivia from '}', they are moved previously to the new method.
var newClassNode = classNode.WithCloseBraceToken(classNode.CloseBraceToken.WithLeadingTrivia()).AddMembers(method).NormalizeWhitespace();
Console.WriteLine(newClassNode.ToString());

This prints the following (exactly your expectation):

public class A
{
#if !SILVERLIGHT
    public int someField;
#endif
    public int CalculateSize()
    {
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM