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