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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM