【问题标题】:Generating semantic code with roslyn使用 roslyn 生成语义代码
【发布时间】:2015-03-06 16:30:19
【问题描述】:

我们试图弄清楚如何使用 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();
}

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

还有一个问题。我们想生成一个自动属性,目前使用SF.AccessorDeclaration,但在转换为完整字符串时会丢失分号。

【问题讨论】:

  • 你的目标是发出代码,对吧?不编译?
  • 是的,但没有明确指定琐事。我也有使用 ReSharper SDK 的一些经验,它的应用领域基本相同。他们确实拥有忽略琐事的工厂,并且它确实打印了所有生成的代码,干净且可编译。我遇到了一些建议使用 CodeDOM 的其他用户的 cmet,据说 Roslyn 不适合这个,但这似乎是有史以来最大的失败 :)
  • 嗯...作为编译器的 Roslyn 是用来消费代码的,而不是生成代码。
  • 对此我不确定。 Roslyn 的构建也是为了在重构等方面支持更好的工具。作为此类重构的作者,我不想处理琐事。正如我所指出的,JetBrains 在那里做得很好。你真的建议说,代码生成最终还是 CodeDOM 和 StringBuilder 的事情吗?似乎不适合我。不要误会我的意思,我会接受这个作为答案,但我仍然宣布这是一个很大的失败。
  • 使用格式化程序绝对是一个好方法。如果你想避开 Workspace 层,你也可以使用SyntaxNode.NormalizeWhitespace()

标签: c# roslyn


【解决方案1】:

您基本上必须添加块定义,然后 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);
        }
    }
}

看起来不错。

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,发现 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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-30
      • 2017-07-13
      • 1970-01-01
      • 2019-08-31
      • 2016-02-01
      • 2018-07-03
      • 2017-03-30
      相关资源
      最近更新 更多