如何使用 Roslyn 将函数插入 VB.NET 中的 class

How to insert a function into a class in VB.NET using Roslyn

我有一些使用 Roslyn 向 class 添加函数的逻辑,它适用于 C# 项目,但不适用于 VB 项目。我正在使用 DocumentEditor 编辑器 class (Microsoft.CodeAnalysis.Editing.DocumentEditor) 来执行更新。

我首先找到对应于 class 定义的 SyntaxNode

在 C# 中,这是一个 ClassDeclarationSyntax 元素。

在 VB 中,这是一个 ClassBlockSyntax 元素。

我在一个字符串变量中生成新函数的完整文本,然后从该文本创建一个 SyntaxNode。

对于C#我使用的方法是CSharpSyntaxTree.ParseText,大致如下:

var Tree = CSharpSyntaxTree.ParseText ( Code, CSharpParseOptions.Default ) ;
var Root = await Tree.GetRootAsync() as CompilationUnitSyntax ;
var Expr = Root.Members.FirstOrDefault()
                       .WithAdditionalAnnotations ( Formatter.Annotation ) ;  

Expr 是 MethodDeclarationSyntax.

类型

对于VB我使用方法VisualBasicSyntaxTree.ParseText,代码几乎相同:

var Tree = VisualBasicSyntaxTree.ParseText ( Code ) ;
var Root = await Tree.GetRootAsync() as CompilationUnitSyntax ;
var Expr = Root.Members.FirstOrDefault() ;

在这种情况下,Expr 的类型为 MethodBlockSyntax

然后我尝试将新节点插入 class。

对于 C#,我使用

RoslynDocEditor.InsertAfter ( RoslynClass.ChildNodes.Last, Expr )    

其中 RoslynClass 是 ClassBlockSyntax 节点,稍后......

RootNode  = RoslynDocEditor.GetChangedRoot()
RootNode  = Formatter.Format ( RootNode, Formatter.Annotation, VSWorkspace )
RoslynDoc = RoslynDoc.WithSyntaxRoot ( RootNode )
ApplyOK   = VSWorkspace.TryApplyChanges ( RoslynDoc.Project.Solution )

这会在 class 的末尾添加新函数。

如果我对 VB 执行相同的操作,它会在第

行生成 InvalidOperationException
RootNode  = RoslynDocEditor.GetChangedRoot()

描述 "The item specified is not the element of a list" 和堆栈跟踪:

at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.NodeListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitClassBlock(ClassBlockSyntax node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.ClassBlockSyntax.Accept[TResult](VisualBasicSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.BaseListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.NodeListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitListElement[TNode](TNode node)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitList[TNode](SyntaxList`1 list)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.NodeListEditor.VisitList[TNode](SyntaxList`1 list)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitCompilationUnit(CompilationUnitSyntax node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.CompilationUnitSyntax.Accept[TResult](VisualBasicSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.BaseListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.NodeListEditor.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.VisualBasic.Syntax.SyntaxReplacer.InsertNodeInList(SyntaxNode root, SyntaxNode nodeInList, IEnumerable`1 nodesToInsert, Boolean insertBefore)
at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode.InsertNodesInListCore(SyntaxNode nodeInList, IEnumerable`1 nodesToInsert, Boolean insertBefore)
at Microsoft.CodeAnalysis.SyntaxNodeExtensions.InsertNodesBefore[TRoot](TRoot root, SyntaxNode nodeInList, IEnumerable`1 newNodes)
at Microsoft.CodeAnalysis.VisualBasic.CodeGeneration.VisualBasicSyntaxGenerator.InsertDeclarationsBeforeInternal(SyntaxNode root, SyntaxNode declaration, IEnumerable`1 newDeclarations)
at Microsoft.CodeAnalysis.VisualBasic.CodeGeneration.VisualBasicSyntaxGenerator._Closure$__310-0._Lambda$__0(SyntaxNode r)
at Microsoft.CodeAnalysis.Editing.SyntaxGenerator.PreserveTrivia[TNode](TNode node, Func`2 nodeChanger)
at Microsoft.CodeAnalysis.VisualBasic.CodeGeneration.VisualBasicSyntaxGenerator.InsertNodesBefore(SyntaxNode root, SyntaxNode declaration, IEnumerable`1 newDeclarations)
at Microsoft.CodeAnalysis.Editing.SyntaxEditor.InsertChange.Apply(SyntaxNode root, SyntaxGenerator generator)
at Microsoft.CodeAnalysis.Editing.SyntaxEditor.GetChangedRoot()
at MultiLang.frmLanguageSwitching.VB$StateMachine_133_btAdd_Click.MoveNext() in C:\VSPackage_Version_7_1\Project\MultiLang\Forms\frmLanguageSwitching.vb:line 944

从 Syntax Visualizer 的屏幕截图中您可以看到 ClassBlock 的最后一个子级是 EndClassStatement,因此使用

更有意义
RoslynDocEditor.InsertBefore ( RoslynClass.ChildNodes.Last, NewFunctionNode )

但这会产生与上述完全相同的错误。

有没有办法以类似的方式在 VB class 中插入一个函数,或者这是否只适用于 C#。

它适用于 VB,如果我将 ClassNode 从 SyntaxNode(返回)转换为 ClassBlockSyntax,然后使用成员 集合

var cbs = ClassNode as Microsoft.CodeAnalysis.VisualBasic.Syntax.ClassBlockSyntax ;
RoslynDocEditor.InsertAfter ( cbs.Members.Last(), Expr ) ;

同样适用于 C#,如果我将 ClassNode 转换为 ClassDeclarationSyntax

var cds = ClassNode as Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax ;
RoslynDocEditor.InsertAfter ( cds.Members.Last(), Expr ) ;

但如前所述,对于 C#,它也适用于

RoslynDocEditor.InsertAfter ( RoslynClass.ChildNodes.Last, Expr ) ;

所以看起来 SyntaxNode.ChildNodes 等同于 C# 的 ClassDeclarationSyntax.Members,但不等同于 ClassBlockSyntax.Members 对于 VB.


编辑

看起来更简单更好的解决方案是使用 AddMember 扩展方法:

RoslynDocEditor.AddMember ( RoslynClass, expr )