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

C# Roslyn-如何在脚本开头插入变量声明语句(使用后)?,c#,refactoring,roslyn,C#,Refactoring,Roslyn,我的目标是解析用户通过Roslyn提供的C#脚本文件。 假设最终用户提供了如下脚本: using System; return "Hello"; 我正在寻找一种通用方法,以便在任何给定脚本中尽可能早的位置插入一些变量初始化语句。 据我所知,这几乎是在最后一次使用语句之后 在本例中,假设我只需要在最早的位置插入“var xyz=123;”。因此,在这种情况下,最终结果应该是 using System; var xyz = 123; return "Hello"; 我怎么能这么做 我尝试了以下方

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

using System;
return "Hello";
我正在寻找一种通用方法,以便在任何给定脚本中尽可能早的位置插入一些变量初始化语句。 据我所知,这几乎是在最后一次使用语句之后

在本例中,假设我只需要在最早的位置插入“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 });
InsertNodesBefore
InsertNodesAfter
处理节点列表,因此要在其之前或之后添加的节点必须位于列表中,并且要插入的节点必须来自同一类型

该方法的注释中提到了它(但不太清楚)

///之后要插入的节点;根节点的后代是列表成员的元素。


如果您愿意,请查看实际执行替换的方法。

我没有搞乱Roslyn,所以这只是一个猜测,但可能是因为C#不允许在使用指令后声明变量(这样的东西是全局变量,C#不支持)。因此,当它解析该行时,它需要另一个使用指令(或名称空间声明)的语句,但得到的却是一个变量声明,这是它不喜欢的。由于我在项目解析选项中显式指定了SourceCodeKind.Script,所以我假设在这些规则下有效的语法转换应该(希望)可以工作?太棒了。非常感谢,非常感谢您的解决方案@Dudi-如果存在多个诊断,我们如何在工作之后或之前创建InsertNodes?因为如果代码修复提供程序对任何一个标记的诊断执行操作,则其他类似诊断的位置将无效。