2016-04-25 124 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);