c# – 使用Roslyn编译语法树
发布时间:2020-12-15 23:48:01 所属栏目:百科 来源:网络整理
导读:我正在尝试使用Roslyn生成和编译包含get / set属性的简单对象的运行时库. 但是,由于某种原因,编译程序集失败,添加Linq名称空间时出错(错误CS0246:找不到类型或名称空间名称’System.Linq'(您是否缺少using指令或程序集引用?)}) . 我曾尝试以多种方式操作生
我正在尝试使用Roslyn生成和编译包含get / set属性的简单对象的运行时库.
但是,由于某种原因,编译程序集失败,添加Linq名称空间时出错(错误CS0246:找不到类型或名称空间名称’System.Linq'(您是否缺少using指令或程序集引用?)}) . 我曾尝试以多种方式操作生成的树并编译每个,但仍然编译失败. 编译成功的唯一方法是将树解析为字符串,然后解析回语法树然后编译. 以下代码执行以下操作: >构建一个包含编译单元,使用,命名空间,类和属性的简单语法树. 代码: 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; } } 解决方法
罗斯林也陷入了这个陷阱. 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); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |