Roslyn - 如何将变量声明语句插入脚本的开头(使用后)?

Roslyn - how to insert variable declaration statements into beginning of script (after usings)?

我的目标是解析用户通过 Roslyn 提供的 C# 脚本文件。 假设最终用户提供如下脚本:

using System;
return "Hello";

我正在寻找在任何给定脚本中尽可能早的位置插入一些变量初始化语句的通用方法。 据我了解,那几乎是在最后一个 using 语句之后。

为了示例,假设我只需要在最早的位置插入 "var xyz = 123;"。所以在这种情况下,最终结果应该是

using System;
var xyz = 123;
return "Hello";

我该怎么做?

我尝试了以下方法;

Solution solution = new AdhocWorkspace().CurrentSolution;
var project = solution.AddProject("projectName", "assemblyName", LanguageNames.CSharp)
     .WithMetadataReferences(new[] {MetadataReference.CreateFromFile(typeof(object).Assembly.Location) })
     .WithParseOptions(new CSharpParseOptions(kind: Microsoft.CodeAnalysis.SourceCodeKind.Script));

// scriptCode contains the user script input, e.g.:
// using System;
// return "Hello";
Document document = project.AddDocument("SCRIPT-TEMP-DOCUMENT.cs", scriptCode);
var root = document.GetSyntaxRootAsync().Result;

var my_statement = SyntaxFactory.ParseStatement("var xyz = 123;");

// will return the node: "using System;"
var last_using = root.DescendantNodes().Where(x => x is UsingDirectiveSyntax).Last();

var documentEditor = DocumentEditor.CreateAsync(document).Result;
documentEditor.InsertAfter(last_using, my_statement);

// This step will throw an exception:
// An exception of type 'System.InvalidCastException' occurred in System.Core.dll but was not handled in user code
// non-English message, so losely translated --> Additional information:  object of type "Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax" cannot be converted to "Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax"
var newDocument = documentEditor.GetChangedDocument();

当我尝试直接替换为

时出现同样的问题
root.InsertNodesAfter(last_using, my_statement);

而不是文档编辑器。

为什么会失败?我不确定为什么它试图将我的语句转换为 using 指令 ​​- 我可以只附加相同类型的节点吗?!

谁能告诉我如何做到最好?

非常感谢!

SyntaxTree tree = CSharpSyntaxTree.ParseText(
    @"using System;
    return 1;", new CSharpParseOptions(LanguageVersion.CSharp6, DocumentationMode.Parse, SourceCodeKind.Script)
);

var root = (CompilationUnitSyntax)tree.GetRoot();
var global = SyntaxFactory.GlobalStatement(SyntaxFactory.ParseStatement("var xyz = 123;"));
root = root.InsertNodesBefore(root.Members.First(), new SyntaxNode[] { global });

InsertNodesBeforeInsertNodesAfter 在节点列表上工作,因此要在其之前或之后添加的节点必须在列表中,并且要插入的节点必须来自同一类型。

方法的注释里有提到(但不是很清楚)

/// <param name="nodeInList">The node to insert after; a descendant of the root node an element of a list member.</param>

如果需要,请参阅实际进行替换的 source code