通过Roslyn编译代码 保存内存中的编译数据并加载执行

nuget

    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
    <PackageReference Include="Mono.Cecil" Version="0.11.6" />
    <PackageReference Include="System.ValueTuple" Version="4.6.1" />

示例一 - 编译后直接加载

参考引用的文章后实现的测试代码

// See https://aka.ms/new-console-template for more information
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Mono.Cecil;
using Mono.Cecil.Rocks;
using System.Reflection;

internal class Program
{
    static void Main(string[] args)
    {
        //string code = "int result = 1 + 2; Console.WriteLine(result);public static void TestValue(){Console.WriteLine(\"sss\");}";
        string code = "using System; public class ExampleClass{ public string getMessage(){ return \"Hello World\"; } }";

        //var options = ScriptOptions.Default.WithImports("System");
        //var script = CSharpScript.Create(code, options);
        //script.RunAsync().Wait();
        //var complierResult = script.Compile();

        CreateAssemblyDefinition(code);

    }

    // 伪代码流程
    public static void CreateAssemblyDefinition(string code)
    {
        // 解析源代码为语法树
        var syntaxTree = new CSharpLanguage().ParseText(code, SourceCodeKind.Regular);

        // 系统核心引用(必须的)
        var references = new List<MetadataReference>
    {
        // 添加系统核心库
        MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
        // 根据情况添加其他系统库,比如:
        MetadataReference.CreateFromFile(typeof(System.Console).Assembly.Location),
        // 如果目标代码使用了ValueTuple,则添加
        MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location)
    };

        // 创建编译对象
        var compilation = new CSharpLanguage()
            .CreateLibraryCompilation("InMemoryAssembly", enableOptimisations: false)
            .AddReferences(references)    // 添加必要程序集引用
            .AddSyntaxTrees(syntaxTree);   // 注入语法树

        // 编译到内存流
        var stream = new MemoryStream();
        var emitResult = compilation.Emit(stream);

        // 用Mono.Cecil读取程序集
        if (emitResult.Success)
        {
            stream.Seek(0, SeekOrigin.Begin);

            // 3. 加载程序集到运行时
            byte[] assemblyBytes = stream.ToArray();
            Assembly runtimeAssembly = Assembly.Load(assemblyBytes);

            // 4. 创建实例并执行方法
            Type exampleClassType = runtimeAssembly.GetType("ExampleClass");
            if (exampleClassType == null)
            {
                throw new InvalidOperationException("类型 ExampleClass 未找到");
            }

            // 创建实例
            object instance = Activator.CreateInstance(exampleClassType);

            // 获取方法信息
            MethodInfo getMessageMethod = exampleClassType.GetMethod("getMessage");
            if (getMessageMethod == null)
            {
                throw new InvalidOperationException("方法 getMessage 未找到");
            }

            // 执行方法并输出结果
            string result = (string)getMessageMethod.Invoke(instance, null);
            Console.WriteLine($"执行结果: {result}");
        }
    }

    public class CSharpLanguage : ILanguageService
    {
        private static readonly LanguageVersion MaxLanguageVersion = Enum
            .GetValues(typeof(LanguageVersion))
            .Cast<LanguageVersion>()
            .Max();

        public SyntaxTree ParseText(string sourceCode, SourceCodeKind kind)
        {
            var options = new CSharpParseOptions(kind: kind, languageVersion: MaxLanguageVersion);

            // Return a syntax tree of our source code
            return CSharpSyntaxTree.ParseText(sourceCode, options);
        }

        public Compilation CreateLibraryCompilation(string assemblyName, bool enableOptimisations)
        {
            // 1. 创建编译选项
            var options = new CSharpCompilationOptions(
                outputKind: OutputKind.DynamicallyLinkedLibrary, // 输出DLL
                optimizationLevel: enableOptimisations ? OptimizationLevel.Release : OptimizationLevel.Debug,
                allowUnsafe: true // 允许不安全代码
            );

            // 2. 创建并返回一个CSharpCompilation对象
            return CSharpCompilation.Create(
                assemblyName, // 程序集名称
                options: options //, // 编译选项
                //references: _references // 程序集引用
            );
        }
    }

    public interface ILanguageService
    {
        SyntaxTree ParseText(string code, SourceCodeKind kind);

        Compilation CreateLibraryCompilation(string assemblyName, bool enableOptimisations);
    }
}

示例二 - 编译后保存到文件 - 从文件中读取后加载

// See https://aka.ms/new-console-template for more information
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;

internal class Program
{
    static void Main(string[] args)
    {
        //string code = "int result = 1 + 2; Console.WriteLine(result);public static void TestValue(){Console.WriteLine(\"sss\");}";
        string code = "using System; public class ExampleClass{ public string getMessage(){ return \"Hello World\"; } }";

        //var options = ScriptOptions.Default.WithImports("System");
        //var script = CSharpScript.Create(code, options);
        //script.RunAsync().Wait();
        //var complierResult = script.Compile();

        CreateAssemblyDefinition(code);

    }

    // 伪代码流程
    public static void CreateAssemblyDefinition(string code)
    {
        // 解析源代码为语法树
        var syntaxTree = new CSharpLanguage().ParseText(code, SourceCodeKind.Regular);

        // 系统核心引用(必须的)
        var references = new List<MetadataReference>
    {
        // 添加系统核心库
        MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
        // 根据情况添加其他系统库,比如:
        MetadataReference.CreateFromFile(typeof(System.Console).Assembly.Location),
        // 如果目标代码使用了ValueTuple,则添加
        MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location)
    };

        // 创建编译对象
        var compilation = new CSharpLanguage()
            .CreateLibraryCompilation("InMemoryAssembly", enableOptimisations: false)
            .AddReferences(references)    // 添加必要程序集引用
            .AddSyntaxTrees(syntaxTree);   // 注入语法树

        // 编译到内存流
        var stream = new MemoryStream();
        var emitResult = compilation.Emit(stream);


        if (emitResult.Success)
        {
            //将stream保存到文件
            using (FileStream fs = new FileStream("InMemoryAssembly.file", FileMode.Create))
            {
                fs.Write(stream.ToArray(), 0, (int)stream.Length);
            }
        }

        //var readRtream = new MemoryStream();
        //readRtream.Seek(0, SeekOrigin.Begin);

        //// 用Mono.Cecil读取程序集

        //// 3. 加载程序集到运行时
        //byte[] assemblyBytes = readRtream.ToArray();

        //加载文件到内存中(模拟从数据库读取)
        byte[] assemblyBytes = File.ReadAllBytes("InMemoryAssembly.file").ToArray();
        Assembly runtimeAssembly = Assembly.Load(assemblyBytes);

        // 4. 创建实例并执行方法
        Type exampleClassType = runtimeAssembly.GetType("ExampleClass");
        if (exampleClassType == null)
        {
            throw new InvalidOperationException("类型 ExampleClass 未找到");
        }

        // 创建实例
        object instance = Activator.CreateInstance(exampleClassType);

        // 获取方法信息
        MethodInfo getMessageMethod = exampleClassType.GetMethod("getMessage");
        if (getMessageMethod == null)
        {
            throw new InvalidOperationException("方法 getMessage 未找到");
        }

        // 执行方法并输出结果
        string result = (string)getMessageMethod.Invoke(instance, null);
        Console.WriteLine($"执行结果: {result}");
    }

    public class CSharpLanguage : ILanguageService
    {
        private static readonly LanguageVersion MaxLanguageVersion = Enum
            .GetValues(typeof(LanguageVersion))
            .Cast<LanguageVersion>()
            .Max();

        public SyntaxTree ParseText(string sourceCode, SourceCodeKind kind)
        {
            var options = new CSharpParseOptions(kind: kind, languageVersion: MaxLanguageVersion);

            // Return a syntax tree of our source code
            return CSharpSyntaxTree.ParseText(sourceCode, options);
        }

        public Compilation CreateLibraryCompilation(string assemblyName, bool enableOptimisations)
        {
            // 1. 创建编译选项
            var options = new CSharpCompilationOptions(
                outputKind: OutputKind.DynamicallyLinkedLibrary, // 输出DLL
                optimizationLevel: enableOptimisations ? OptimizationLevel.Release : OptimizationLevel.Debug,
                allowUnsafe: true // 允许不安全代码
            );

            // 2. 创建并返回一个CSharpCompilation对象
            return CSharpCompilation.Create(
                assemblyName, // 程序集名称
                options: options //, // 编译选项
                                 //references: _references // 程序集引用
            );
        }
    }

    public interface ILanguageService
    {
        SyntaxTree ParseText(string code, SourceCodeKind kind);

        Compilation CreateLibraryCompilation(string assemblyName, bool enableOptimisations);
    }
}

封装后的帮助类

public class CompileHelper
{
    // 伪代码流程
    public static MemoryStream CreateAssemblyDefinition(string code)
    {
        // 解析源代码为语法树
        var syntaxTree = new CSharpLanguage().ParseText(code, SourceCodeKind.Regular);

        // 系统核心引用(必须的)
        var references = new List<MetadataReference>
        {
            // 添加系统核心库
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            // 根据情况添加其他系统库,比如:
            MetadataReference.CreateFromFile(typeof(System.Console).Assembly.Location),
            // 如果目标代码使用了ValueTuple,则添加
            MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location)
        };

        // 创建编译对象
        var compilation = new CSharpLanguage()
            .CreateLibraryCompilation("InMemoryAssembly", enableOptimisations: false)
            .AddReferences(references)    // 添加必要程序集引用
            .AddSyntaxTrees(syntaxTree);   // 注入语法树

        // 编译到内存流
        var stream = new MemoryStream();
        var emitResult = compilation.Emit(stream);

        if (!emitResult.Success)
        {
            //emitResult.Diagnostics
            throw new Exception("CompileError");
        }

        return stream;
    }

    public static void ReadStream(MemoryStream stream, string saveFile)
    {
        //将stream保存到文件
        using (FileStream fs = new FileStream(saveFile, FileMode.Create))
        {
            fs.Write(stream.ToArray(), 0, (int)stream.Length);
        }
    }

    public static void Compiling2File(string code, string saveFile)
    {
        var stream = CreateAssemblyDefinition(code);
        ReadStream(stream, saveFile);
    }

    public static Assembly LoadAssemblyFromFile(string filePath)
    {
        //加载文件到内存中(模拟从数据库读取)
        byte[] assemblyBytes = File.ReadAllBytes(filePath).ToArray();
        Assembly runtimeAssembly = Assembly.Load(assemblyBytes);
        return runtimeAssembly;
    }

    public static Assembly LoadAssemblyFromFile(byte[] assemblyBytes)
    {
        Assembly runtimeAssembly = Assembly.Load(assemblyBytes);
        return runtimeAssembly;
    }

    public static Type GetClass(string filePath, string className)
    {
        Assembly runtimeAssembly = LoadAssemblyFromFile(filePath);

        //创建实例并执行方法
        Type exampleClassType = runtimeAssembly.GetType(className);
        if (exampleClassType == null)
        {
            throw new InvalidOperationException($"ClassNotFound");
        }

        return exampleClassType;
    }

    public static Type GetClass(Assembly runtimeAssembly, string className)
    {
        Type exampleClassType = runtimeAssembly.GetType(className);
        if (exampleClassType == null)
        {
            throw new InvalidOperationException($"ClassNotFound");
        }

        return exampleClassType;
    }

    public static object? ExecMethod(string filePath, string className, string methodName, object?[]? parameter = null)
    {
        var exampleClassType = GetClass(filePath, className);

        // 创建实例
        object instance = Activator.CreateInstance(exampleClassType);

        // 获取方法信息
        MethodInfo getMessageMethod = exampleClassType.GetMethod(methodName);
        if (getMessageMethod == null)
        {
            throw new InvalidOperationException("MethodNotFound");
        }

        // 执行方法并输出结果
        var result = getMessageMethod.Invoke(instance, parameter);
        //Console.WriteLine($"执行结果: {result}");
        return result;
    }

    public static object? ExecMethod(Type classType, string methodName, object?[]? parameter = null)
    {
        // 创建实例
        object instance = Activator.CreateInstance(classType);

        // 获取方法信息
        MethodInfo getMessageMethod = classType.GetMethod(methodName);
        if (getMessageMethod == null)
        {
            throw new InvalidOperationException("MethodNotFound");
        }

        // 执行方法并输出结果
        var result = getMessageMethod.Invoke(instance, parameter);
        //Console.WriteLine($"执行结果: {result}");
        return result;
    }
}

扩展封装

public class CompileBase
{
    /// <summary>
    /// 获取编译后的字节数据
    /// </summary>
    /// <param name="code"></param>
    /// <param name="saveFile"></param>
    /// <returns></returns>
    public byte[] Compiling(string code, string saveFile)
    {
        var memStream = CompileHelper.CreateAssemblyDefinition(code);
        var byteArray = new byte[memStream.Length];
        var count = memStream.Read(byteArray, 0, 20);

        // Read the remaining bytes, byte by byte.
        while (count < memStream.Length)
        {
            byteArray[count++] = (byte)memStream.ReadByte();
        }

        return byteArray;
    }

    public Assembly LoadAssemblyFromFile(byte[] assemblyBytes)
    {
        return CompileHelper.LoadAssemblyFromFile(assemblyBytes);
    }

    public Type GetClass(Assembly assembly, string className)
    {
        var classInfo = CompileHelper.GetClass(assembly, className);

        return classInfo;
    }

    public static object? ExecMethod(Type classType, string methodName, object?[]? parameter = null)
    {
        // 执行方法并输出结果
        var result = CompileHelper.ExecMethod(classType, methodName, parameter);
        //Console.WriteLine($"执行结果: {result}");
        return result;
    }
}

[参考]
In-memory C# compilation (and .dll generation) using Roslyn

posted @ 2025-08-07 18:23  Hey,Coder!  阅读(14)  评论(0)    收藏  举报