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

 

上一篇:asp.net 反射reflection(原理读元数据,3种加载方法,反射的几种调用方法,反射在MVC,ORM中的应用)


下一篇:.netCore各种通过反射加载程序集的方法的总结