2016-04-25 13 views
1

Ich versuche Roslyn zu verwenden, um eine Laufzeitbibliothek einfacher Objekte zu generieren und zu kompilieren, die get/set-Eigenschaften enthalten.Kompilieren eines Syntaxbaums mit Roslyn

Aus irgendeinem Grund schlägt das Kompilieren der Assembly mit dem Fehler fehl, den Linq-Namespace hinzuzufügen (Fehler CS0246: Der Typ oder Namespacename 'System.Linq' konnte nicht gefunden werden (fehlt eine using-Direktive oder eine Assemblyreferenz) ?)}).

Ich habe versucht, den generierten Baum auf verschiedene Arten zu manipulieren und zu kompilieren, aber die Kompilierung schlägt fehl.

Der einzige Weg, auf dem die Kompilierung erfolgreich ist, besteht darin, dass der Baum zu einem String analysiert wird, dann in einen Syntaxbaum geparst und dann kompiliert wird.

Der folgende Code führt Folgendes aus:

  1. Erstellen Sie eine einfache Syntax-Baum enthält Kompilierungseinheit, usings, Namespace, Klasse und Eigentum.
  2. Versuchen Sie, den Baum (nicht)
  3. Neuen Syntaxbaum mit C# 6 Option und kompiliert (versagt)
  4. Format Syntaxbaum und kompiliert (versagt)
  5. Serialize Baum zu bespannen kompilieren, dann verwenden SyntaxFactory .ParseSyntaxTree und kompilieren den erzeugten Baum (Erfolg)

Der Code:

private static readonly CSharpCompilationOptions DefaultCompilationOptions = 
     new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) 
       .WithOverflowChecks(true) 
       .WithPlatform(Platform.X86) 
       .WithOptimizationLevel(OptimizationLevel.Release) 
       .WithUsings(DefaultNamespaces); 
    private static readonly IEnumerable<string> DefaultNamespaces = 
     new[] 
     { 
        "System", 
        "System.IO", 
        "System.Net", 
        "System.Linq", 
        "System.Text", 
        "System.Text.RegularExpressions" 
     }; 

    private static readonly IEnumerable<MetadataReference> DefaultReferences = 
     new[] 
     { 
        MetadataReference.CreateFromFile(typeof (object).Assembly.Location), 
        MetadataReference.CreateFromFile(typeof (System.Linq.Enumerable).Assembly.Location), 
        MetadataReference.CreateFromFile(typeof (System.GenericUriParser).Assembly.Location), 
        MetadataReference.CreateFromFile(typeof (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.Location) 
     }; 

    static void Main(string[] args) 
    { 
     MakeAssembly(); 
     Console.ReadLine(); 
    } 

    private static void MakeAssembly() 
    { 
     //Compilation Unit and Usings 
     CompilationUnitSyntax cu = SyntaxFactory.CompilationUnit() 
      .AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("System")), 
      SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(System.Linq.Enumerable).Namespace))) 
     ; 

     // NameSpace 
     NamespaceDeclarationSyntax ns = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.IdentifierName("Roslyn")); 

     // Class 
     ClassDeclarationSyntax classNode = SyntaxFactory.ClassDeclaration("MyClass") 
         .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) 
        ; 

     // Property 
     classNode= classNode.AddMembers(
           SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName("Int32"), "MyProperty") 
             .AddAccessorListAccessors(
             SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), 
             SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))). 
             AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); 
     ns = ns.AddMembers(classNode); 
     cu = cu.AddMembers(ns); 

     // Try To Compile Syntax Tree root 
     var root = cu.SyntaxTree.GetRoot(); 
     var st = root.SyntaxTree; 
     var assembly = CompileAndLoad(st); 

     if (assembly != null) 
     { 
      Console.WriteLine("Success compile syntax tree root"); 
      return; 
     } 
     else 
      Console.WriteLine("failed to compile syntax tree root"); 

     // Try to compile new syntax tree 
     var stNew = SyntaxFactory.SyntaxTree(cu, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)); 
     assembly = CompileAndLoad(stNew); 
     if (assembly != null) 
     { 
      Console.WriteLine("Success compile new syntax tree"); 
      return; 
     } 
     else 
      Console.WriteLine("failed to compile new syntax tree"); 

     // Try to format node 
     AdhocWorkspace cw = new AdhocWorkspace(); 
     OptionSet options = cw.Options; 
     options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInMethods, false); 
     options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInTypes, false); 

     SyntaxNode formattedNode = Formatter.Format(cu, cw, options); 
     var stFormat = SyntaxFactory.SyntaxTree(cu, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)); 
     assembly = CompileAndLoad(stFormat); 
     if (assembly != null) 
     { 
      Console.WriteLine("Success compile formatted syntax tree"); 
      return; 
     } 
     else 
      Console.WriteLine("failed to compile formatted syntax tree"); 


     // Try to serialize and parse 
     StringBuilder sb = new StringBuilder(); 
     using (StringWriter writer = new StringWriter(sb)) 
     { 
      formattedNode.WriteTo(writer); 
     } 
     var treeAsString = sb.ToString(); 
     var stParsed = SyntaxFactory.ParseSyntaxTree(treeAsString); 
     assembly = CompileAndLoad(stParsed); 
     if (assembly != null) 
     { 
      Console.WriteLine("Success compile parsed syntax tree"); 
      return; 
     } 
     else 
      Console.WriteLine("failed to compile formatted syntax tree"); 

    } 

    private static Assembly CompileAndLoad(SyntaxTree st) 
    { 
     var compilation 
      = CSharpCompilation.Create("TestRoslyn.dll", new SyntaxTree[] { st }, null, DefaultCompilationOptions); 
     compilation = compilation.WithReferences(DefaultReferences); 
     using (var stream = new MemoryStream()) 
     { 
      EmitResult result = compilation.Emit(stream); 
      if (result.Success) 
      { 
       var assembly = Assembly.Load(stream.GetBuffer()); 
       return assembly; 
      } 
      return null; 
     } 
    } 

Antwort

6

ich in diese Falle fiel mit R oslyn auch. Die using-Direktive wird nicht nur als String ausgedrückt, sondern jeder Teil des qualifizierten Namens ist ein Syntax-Node. Sie benötigen Knoten wie dieser

var qualifiedName= SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("System"),  
               SyntaxFactory.IdentifierName("Linq"));  
var usingDirective = SyntaxFactory.UsingDirective(qualifedName); 

ich eine Hilfsmethode erstellen hat einen String an die korrekte Syntax Knoten zu konvertieren.

private UsingDirectiveSyntax CreateUsingDirective(string usingName) 
{ 
    NameSyntax qualifiedName = null; 

    foreach (var identifier in usingName.Split('.')) 
    { 
     var name = SyntaxFactory.IdentifierName(identifier); 

     if (qualifiedName != null) 
     { 
      qualifiedName = SyntaxFactory.QualifiedName(qualifiedName, name); 
     } 
     else 
     { 
      qualifiedName = name; 
     } 
    } 

    return SyntaxFactory.UsingDirective(qualifiedName); 
} 
+0

funktioniert! Vielen Dank, hat mir den Kopf gebrochen. –

1

Sie können SyntaxFactory.ParseName verwenden, die die Zeichenfolge Parsen und dann den Knoten qualifizierten Namen Syntax für Ihre mit Richtlinien Aufbau wird handhaben:

var qualifiedName = SyntaxFactory.ParseName("System.Linq"); 
var usingDirective = SyntaxFactory.UsingDirective(qualifiedName);