2015-01-08 27 views
8

Proviamo a capire come generare codice con Roslyn. Non sto parlando di qualcosa come CSharpSyntaxTree.ParseText che prenderà alcune stringhe e le convertirà in un AST. Invece, vorrei costruire il mio modello in qualche modo come questo (pseudo codice):Generazione del codice semantico con roslyn

  1. Creare file come unità di compilazione
  2. Aggiungi classe MyClass-file
  3. metodo Add DoSomething-MyClass
  4. Set corpo DoSomething in modo simile al System.Linq.Expressions

Abbiamo scoperto di recente Microsoft.CodeAnalysis.CSharp.SyntaxFactory, e sembrava essere promettente. Tuttavia, ovviamente dobbiamo aggiungere noi stessi banalità.

Dopo la creazione di un albero con SyntaxFactory.CompilationUnit() e l'aggiunta di alcuni membri avanti e indietro, l'output di ToFullString() è solo un mucchio di testo, che non è né leggibile, né compilabile (ad esempio, parentesi graffe mancanti). Ci manca qualcosa durante la generazione del testo dal modello?

EDIT:

Quando si utilizza aree di lavoro, è possibile impostare le opzioni che influenzano il comportamento di spazi bianchi:

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(); 
} 

Questo produce già un risultato migliore. Qualcuno può confermare questo come una buona soluzione o è piuttosto un hack?

Un problema rimane. Vogliamo generare una proprietà automatica, che attualmente utilizza SF.AccessorDeclaration ma manca il punto e virgola quando si converte la stringa completa.

+0

Il tuo obiettivo è emettere il codice, giusto? non lo compila? – i3arnon

+0

Sì, ma senza specificare esplicitamente la banalità. Ho anche qualche esperienza con ReSharper SDK, che ha fondamentalmente lo stesso campo di applicazione. Loro * fanno * hanno fabbriche che sono ignare di curiosità, e stampa tutto il codice generato pulito e compilabile. Ho incrociato alcuni commenti di altri utenti che suggeriscono di usare CodeDOM, e che Roslyn non è adatto a questo, ma questo sembrerebbe il più grande fallimento di sempre :) – Matthias

+0

Beh ... Roslyn, essendo un compilatore, ha lo scopo di consumare codice, non produrlo – i3arnon

risposta

10

Che, fondamentalmente, deve aggiungere definizioni di blocco, quindi Roslyn gestisce la curiosità per voi fino a quando si utilizza il Formatter (come hai scritto)

Ecco un esempio di una semplice classe che viene generato correttamente senza a dover specificare qualsiasi curiosità

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()); 

Nota: Sintassi = Microsoft.CodeAnalysis.CSharp.SyntaxFactory

Questo genera il seguente cl ass definiton:

class MyClass 
{ 
    void Method() 
    { 
     Console.WriteLine("Goodbye everyone!"); 
    } 

    int Property 
    { 
     get 
     { 
      return default(int); 
     } 
    } 
} 

Sembra ok.