c# - DocumentEditor InsertBefore 似乎无法在文档中找到节点

标签 c# visual-studio roslyn

我正在使用 roslyn 为 Visual Studio 开发代码重构。

我正在使用 DocumentEditor 类删除一些 SyntaxNode,在删除它们之后我将插入一些新的。我正在使用我知道我不会删除的 syntaxNode 作为我插入新 SyntaxNode 的位置的引用。但是我用的时候小编好像找不到

foreach (var nodeToRemove in nodesToRemove)
{
    editor.RemoveNode(directive);
}

editor.InsertBefore(untouchedNode, newNodesToAdd); // No exception here, only when getting the new document

为了调试它,我试图找到引用节点,然后像编辑器一样继续

var ns = editor.OriginalRoot.DescendantNodesAndSelf().OfType<NamespaceDeclarationSyntax>().FirstOrDefault(); // ns is not null
var IshouldExist = editor.OriginalRoot.GetCurrentNode(ns); //it is null, weird

我不知道为什么 GetCurrentNode() 在这里返回 null。

在堆栈跟踪中,我注意到插入是在更改的文档中完成的,我不认为这应该是一个问题,因为如果我不尝试插入新节点,我正在使用的 SytaxNode因为引用仍在更改的文档中。

And throws an exception with the following stacktrace:
System.InvalidCastException : Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax' to type 'Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax'.
   at System.Linq.Enumerable.<CastIterator>d__97`1.MoveNext()
   at System.Collections.Generic.List`1.InsertRange(Int32 index,IEnumerable`1 collection)
   at Microsoft.CodeAnalysis.SyntaxList`1.InsertRange(Int32 index,IEnumerable`1 nodes)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.NodeListEditor.VisitList[TNode](SyntaxList`1 list)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCompilationUnit(CompilationUnitSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.BaseListEditor.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.NodeListEditor.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.InsertNodeInList(SyntaxNode root,SyntaxNode nodeInList,IEnumerable`1 nodesToInsert,Boolean insertBefore)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode.InsertNodesInListCore(SyntaxNode nodeInList,IEnumerable`1 nodesToInsert,Boolean insertBefore)
   at Microsoft.CodeAnalysis.SyntaxNodeExtensions.InsertNodesBefore[TRoot](TRoot root,SyntaxNode nodeInList,IEnumerable`1 newNodes)
   at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.InsertNodesBeforeInternal(SyntaxNode root,SyntaxNode declaration,IEnumerable`1 newDeclarations)
   at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.<>c__DisplayClass183_0.<InsertNodesBefore>b__0(SyntaxNode r)
   at Microsoft.CodeAnalysis.Editing.SyntaxGenerator.PreserveTrivia[TNode](TNode node,Func`2 nodeChanger)
   at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.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 Microsoft.CodeAnalysis.Editing.DocumentEditor.GetChangedDocument()

我做错了什么?

最佳答案

今天早些时候,我自己也遇到了类似的问题。我没有使用 DocumentEditor,而是在 Document 对象本身上以更“原始”的性质执行操作,但是,当我尝试通过自定义谓词对 Using Directives 进行排序时,我注意到如果我清除所有旧的在插入新使用之前使用指令,使用 *.InsertBefore 或 *.InsertAfter,当引用节点不是 Using 指令时,整个系统就会崩溃。 (看起来在内部它可能试图将引用节点视为 UsingDirectiveSyntax 对象,然后无法比较它,但这纯粹是我基于异常详细信息的猜想...)

如果您正在使用不再有 Using 语句的 Document 对象,您将需要考虑使用不同的方法来插入 using 语句:

List<UsingDirectiveSyntax> myNewUsingList = /* Construct Using List somehow */
NameSpaceDeclarationSyntax newNameSpace = myOldNameSpace.AddUsings(myUsingList);
CompilationUnitSyntax newCompileRootSyntax = (documentRoot as CompilationUnitSyntax).ReplaceNode(myOldNameSpace, newNameSpace);
return document.WithSyntaxRoot(newCompileRootSyntax);

这与使用 DocumentEditor 实例时使用的语义不同,但由于我对正在修改的当前文档的节点遍历非常不熟悉,所以我不知道在将这种单独的方法转换为您的需求。

这对我来说是一种烦人的代码,因为这种方法是在不可变对象(immutable对象)上工作,所以上面调用的所有上面返回一个被操纵对象的新实例,并通过实现的方法参数进行定向修改。上面的部分代码,

document.WithSyntaxRoot(/*variable*/);

创建一个全新的 Document() 对象,其内部数据表示从旧文档到新文档的更改,以及您使用先前代码发起的所有替换、插入、删除等操作。

基于对基于 DocumentEditor 的方法的一些初步吐槽,使用 VS 2019 的 SDK 工具创建 CodeRefactoring 项目,DocumentEditor 方法可能看起来更像这样:

DocumentEditor ed = await DocumentEditor.CreateAsync(document, cancellationToken);
NamespaceDeclarationSyntax nameSpace = ed.GetChangedRoot()
  .DescendantNodes()
  .FirstOrDefault(t => t.GetType() == typeof(NamespaceDeclarationSyntax)) as NamespaceDeclarationSyntax;

if (nameSpace != null)
{
  NamespaceDeclarationSyntax modifiedWithMyUsings = nameSpace.AddUsings(myUsingLists);
  ed.ReplaceNode(nameSpace, modifiedWithMyUsings);
}

关于c# - DocumentEditor InsertBefore 似乎无法在文档中找到节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55782974/

相关文章:

c++ - 当你的程序在进入 main 之前崩溃了怎么办?

c# - 是否可以在 switch 语句中使用 nameof 表达式?

c# - 为什么 Socket.AcceptAsync 没有触发 SocketAsyncEventArgs Completed 事件?

c# - LINQ- 'the type of one of the expressions in the join clause is incorrect'

asp.net - VS安装过程中找不到符合以下参数的产品

c# - 在 Roslyn 中加载可移植 MetadataReference 的正确方法是什么?

c# - 可能是Visual Studio 2015中的C#编译器错误

C# UWP 通用 Windows 应用程序 - 调整形状大小

c# - 从通过单元测试项目调用的类库访问 App.Config 设置

windows - 使用 Visual C++ 2005/2008/2010 的可移植 Windows 应用程序(不是源代码!)?