2016-04-25 43 views
1

我正在嘗試使用Roslyn來生成和編譯包含get/set屬性的簡單對象的運行時庫。使用Roslyn編譯語法樹

但是,由於某些原因,編譯程序集時失敗,錯誤添加了Linq名稱空間(錯誤CS0246:無法找到類型或名稱空間名稱'System.Linq'(您是否缺少using指令或程序集引用?)})。

我試過用各種方法操作生成的樹並編譯每一個,但仍然編譯失敗。

編譯成功的唯一方法是如果樹被解析爲字符串,然後解析回語法樹,然後編譯。

下面的代碼執行以下操作:

  1. 構建包含編譯單元,usings,命名空間,類和屬性的簡單語法樹。
  2. 嘗試編譯樹(失敗)
  3. 生成新的語法樹與C#6選項並編譯(失敗)
  4. 格式語法樹和編譯(失敗)
  5. 序列化樹串,然後用SyntaxFactory .ParseSyntaxTree和編譯生成的樹(成功)

的代碼:

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

回答

6

我掉進這個陷阱,其中R奧斯林也。 using指令不僅表示爲字符串,限定名稱的每個部分都是語法節點。你需要像這樣創建你的節點

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

我寫了一個輔助方法來將字符串轉換爲正確的語法節點。

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

作品!非常感謝,在這個問題上我突然想到了。 –

1

您可以使用SyntaxFactory.ParseName將處理解析字符串,然後建立合格的名稱語法節點爲您使用指令

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