使用 Roslyn引擎动态编译代码
Roslyn引擎自2014年开源至今这么久,一直没怎么了解过,虽然VS2015早就集成了它。
以前老一套的动态编译方法在 .NET Core中似乎不再支持了,很多方法都是未实现的。下面就介绍如何在.NET Core环境中使用Roslyn进行动态编译。话不多说,Talk is cheap, show me the code.
首先是安装nuget包
Install-Package Microsoft.CodeAnalysis.CSharp
接下来是我们需要动态编译和执行的代码:
// 表达式树 SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { Console.WriteLine(message); } } }");
紧接着是创建编译对象:
// 随机程序集名称 string assemblyName = Path.GetRandomFileName(); // 元数据引用 MetadataReference[] references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; // 创建编译对象 CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
然后是编译:
using (var ms = new MemoryStream()) { // 将编译后的IL代码放入内存中 EmitResult result = compilation.Emit(ms); // 编译失败,提示 if (!result.Success) { IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } } else { // 编译成功则从内存中加载程序集 ms.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(ms.ToArray()); } }
最后则是调用:
// 反射获取程序集中 的类 Type type = assembly.GetType("RoslynCompileSample.Writer"); // 创建该类的实例 object obj = Activator.CreateInstance(type); // 通过反射方式调用类中的方法。(Hello World 便是我们传入方法的参数) type.InvokeMember("Write", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { "Hello World" });
最后咱们可以封装一个方法,以便于调用:
/// <summary> /// 动态编译 /// </summary> /// <param name="code">需要动态编译的代码</param> /// <returns>动态生成的程序集</returns> public static Assembly GenerateAssemblyFromCode(string code) { Assembly assembly = null; // 丛代码中转换表达式树 SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code); // 随机程序集名称 string assemblyName = Path.GetRandomFileName(); // 引用 var references = AppDomain.CurrentDomain.GetAssemblies().Select(x => MetadataReference.CreateFromFile(x.Location)); // 创建编译对象 CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var ms = new MemoryStream()) { // 将编译好的IL代码放入内存流 EmitResult result = compilation.Emit(ms); // 编译失败,提示 if (!result.Success) { IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } } else { // 编译成功,从内存中加载编译好的程序集 ms.Seek(0, SeekOrigin.Begin); assembly = Assembly.Load(ms.ToArray()); } } return assembly; }