C# CodeAnalysis - 表达式的结构

C# CodeAnalysis - Structure of expressions

我想在 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 位置的信息。因此,如果没有丢失的信息,则无法进行替换。

不使用前缀@的情况下我已经检查过了,但是结果是一样的。有人可以告诉我我做错了什么吗?

SOLUTION 正如 JoshVarty 在他的评论之一中所建议的那样:在 NuGet 包中禁用脚本的代码分析之前,我必须使用一种解决方法。首先,我把 string script = "..." 装饰成

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

@GetInt重写完成后,我把添加的装饰去掉

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

如果您想解析单个表达式,可以使用 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 似乎已被删除。我能想到的唯一解决方法是将每个语句包装在 class 和一个方法中,直到它再次开始工作。

它出现在 http://sourceroslyn.io 上的原因是它基于当前的母版,他们正在重新添加它。