0
点赞
收藏
分享

微信扫一扫

通过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);
    }
}

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

留待后查,同时方便他人



举报

相关推荐

0 条评论