我想在VS2013中的C#(.NET 4.5)脚本中进行简单的代码替换。每个@GetIt调用都应该重写,以便将其封装在lambda函数中:

new MyClass(@GetInt("a") * @GetInt("b"));


变成

new MyClass( x => (x.GetInt("a") * x.GetInt("b")) )


我安装了Roslyn的CodeAnalysis来解析脚本。我使用以下调用来获取所有带有@GetInt标识符的令牌:

var getters = CSharpSyntaxTree.ParseText("new MyClass(@GetInt("a") * @GetInt("b"));")
                              .GetRoot().DescendantTokens().OfType<SyntaxToken>()
                              .Where(x => x.Text.Equals("@GetInt"));


对于@GetInt作为MyClass构造函数的简单参数,它可以正常工作,并且使用getters[i].Parent.Parent.Parent.Parent我可以正确地到达构造函数的MethodDeclarationSyntax节点。

但是,像上面的示例中那样添加乘法,第二个@GetInt的令牌将* GetInt("b")声明为其父级(但是,它已经是MethodDeclarationSyntax,而不是参数节点)并遍历其父级会导致CompilationUnitSyntax是根!

这样,我就没有获得有关语法树中第二个@GetInt位置的信息。因此,如果没有丢失的信息,将无法进行替换。

我已经检查过这种情况,没有使用前缀@,但是结果是相同的。有人可以告诉我我做错了什么吗?

JoshVarty在他的评论之一中建议的解决方案:在NuGet软件包中禁用Scripts的代码分析之前,我必须使用一种解决方法。首先,我将string script = "..."并装饰为

var decoratedScript = "class MYCLASS { void METHOD() {\n" + script + "\n} }";


@GetInt的重写完成后,我删除了添加的装饰。

最佳答案

默认情况下,ParseText()希望您传递典型的C#文档。 (由使用语句,名称空间,类型等组成的东西。)

如果要解析单个表达式,可以使用CSharpParseOptions进行解析:

var parseOptions = CSharpParseOptions.Default;
parseOptions = parseOptions.WithKind(SourceCodeKind.Script); //We're going to be passing individual expressions in.

var getters = CSharpSyntaxTree.ParseText(@"new MyClass(@GetInt(""a"") * @GetInt(""b""));", parseOptions)
                  .GetRoot().DescendantTokens().OfType<SyntaxToken>()
                  .Where(x => x.Text.Equals("@GetInt"));


使用此方法时,我会同时将@GetInt的两个调用作为BinaryExpressionSyntax的子级(乘法)。

您可以通过以下方式查看该树最初是否为您自己错误地解析:

var tree = CSharpSyntaxTree.ParseText(@"new MyClass(@GetInt(""a"") * @GetInt(""b""));");
var errs = tree.GetDiagnostics().Where(n => n.Severity == DiagnosticSeverity.Error);


编辑真的很抱歉,似乎已为RTM删除了脚本API。我可以想到的唯一解决方法是将每个语句包装在一个类和一个方法中,直到再次开始起作用。

它出现在http://source.roslyn.io上的原因是因为它基于当前的主服务器,因此他们正在重新添加它。

关于c# - C#CodeAnalysis-表达式的结构,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32034881/

10-09 08:07