使用 roslyn 生成语义代码

本文关键字:语义 代码 roslyn 使用 | 更新日期: 2023-09-27 18:36:41

我们试图弄清楚如何使用Roslyn生成代码。我不是在谈论像CSharpSyntaxTree.ParseText这样的东西,它会接受一些字符串并将它们转换为 AST。相反,我想以某种方式构建我的模型,如下所示(伪代码):

  1. 创建file作为编译单元
  2. 将类MyClass添加到file
  3. 将方法DoSomething添加到MyClass
  4. 以类似于System.Linq.Expressions的方式设置DoSomething正文

我们最近发现了Microsoft.CodeAnalysis.CSharp.SyntaxFactory,它似乎很有希望。但是,显然我们必须自己添加琐事。

在用SyntaxFactory.CompilationUnit()构建一棵树并来回添加一些成员后,ToFullString()的输出只是一堆文本,既不可读,也不可编译(例如,缺少大括号)。从模型生成文本时,我们是否遗漏了某些内容?

编辑:

使用工作区时,可以设置影响空格行为的选项:

public string Generate (CompilationNode rootNode)
{
  var cw = new CustomWorkspace();
  cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true);
  var formattedCode = Formatter.Format (CreateFile(rootNode), cw);
  return formattedCode.ToFullString();
}

这已经产生了更好的结果。有人可以确认这是一个很好的解决方案还是一个黑客?

一个问题仍然存在。我们想生成一个 auto-property,当前正在使用 SF.AccessorDeclaration但在转换为完整字符串时它错过了分号。

使用 roslyn 生成语义代码

你基本上必须添加块定义,然后 Roslyn 为你处理琐事,只要你使用格式化程序(正如你所写的)

这是一个正确生成的简单类的示例,而无需我自己指定任何琐事

var consoleWriteLine = Syntax.MemberAccessExpression(
      SyntaxKind.SimpleMemberAccessExpression,
      Syntax.IdentifierName("Console"),
      name: Syntax.IdentifierName("WriteLine"));
  var arguments = Syntax.ArgumentList (
      Syntax.SeparatedList (
          new[]
          {
              Syntax.Argument (
                  Syntax.LiteralExpression (
                      SyntaxKind.StringLiteralExpression,
                      Syntax.Literal (@"""Goodbye everyone!""", "Goodbye everyone!")))
          }));
  var consoleWriteLineStatement = Syntax.ExpressionStatement (Syntax.InvocationExpression (consoleWriteLine, arguments));
  var voidType = Syntax.ParseTypeName ("void");
  var method = Syntax.MethodDeclaration (voidType, "Method").WithBody (Syntax.Block(consoleWriteLineStatement));
  var intType = Syntax.ParseTypeName ("int");
  var getterBody = Syntax.ReturnStatement (Syntax.DefaultExpression (intType));
  var getter = Syntax.AccessorDeclaration (SyntaxKind.GetAccessorDeclaration, Syntax.Block (getterBody));
  var property = Syntax.PropertyDeclaration (intType, "Property").WithAccessorList (Syntax.AccessorList (Syntax.SingletonList (getter)));
  var @class = Syntax.ClassDeclaration ("MyClass").WithMembers (Syntax.List (new MemberDeclarationSyntax[] { method, property }));
  var cw = new CustomWorkspace();
  cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true);
  var formattedCode = Formatter.Format (@class, cw);
  Console.WriteLine (formattedCode.ToFullString());

注意:语法 = Microsoft.CodeAnalysis.CSharp.SyntaxFactory

这将生成以下类定义:

class MyClass
{
    void Method()
    {
        Console.WriteLine("Goodbye everyone!");
    }
    int Property
    {
        get
        {
            return default(int);
        }
    }
}

似乎没问题。

我遇到了同样的问题,发现CustomWorkspace现在被称为AdhocWorkspace

var cw = new AdhocWorkspace();
cw.Options.WithChangedOption(CSharpFormattingOptions.IndentBraces, true);
var formatter = Formatter.Format(cu, cw);
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
    formatter.WriteTo(writer);
}
var code = sb.ToString();