C# 动态脚本执行器

环境:net8.0

ScriptExecutor.cs

cs 复制代码
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using System.Text;

    /// <summary>
    /// C#动态脚本执行器
    /// </summary>
    public class ScriptExecutor
    {
        private Assembly? _assembly;
        private readonly Dictionary<string, object> _instances = new();

        /// <summary>
        /// 编译动态 C# 代码
        /// </summary>
        /// <param name="code">完整 C# 代码</param>
        public void Compile(string code)
        {
            var syntaxTree = CSharpSyntaxTree.ParseText(code);

            var references = new List<MetadataReference>
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Task).Assembly.Location),
                MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location)
            };

            var compilation = CSharpCompilation.Create(
                assemblyName: "DynamicAssembly",
                syntaxTrees: new[] { syntaxTree },
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            );

            using var ms = new System.IO.MemoryStream();
            var result = compilation.Emit(ms);

            if (!result.Success)
            {
                var errors = new StringBuilder();
                foreach (var diag in result.Diagnostics)
                    errors.AppendLine(diag.ToString());
                throw new Exception("动态编译失败:\n" + errors);
            }

            ms.Seek(0, System.IO.SeekOrigin.Begin);
            _assembly = Assembly.Load(ms.ToArray());
        }

        /// <summary>
        /// 调用动态代码方法(支持实例/静态/异步/带参数)
        /// </summary>
        /// <param name="fullClassName">命名空间+类名</param>
        /// <param name="methodName">方法名</param>
        /// <param name="parameters">方法参数</param>
        /// <param name="singletonInstance">是否共享实例状态(true:同一类共享同一实例)</param>
        /// <returns>方法执行结果</returns>
        public async Task<object?> InvokeAsync(string fullClassName, string methodName, object[]? parameters = null, bool singletonInstance = true)
        {
            if (_assembly == null)
                throw new Exception("程序集未编译,请先调用 Compile()");

            var type = _assembly.GetType(fullClassName) ?? throw new Exception($"未找到类型: {fullClassName}");
            var method = type.GetMethod(methodName) ?? throw new Exception($"未找到方法: {methodName}");

            // 获取或创建实例
            object? instance = null;
            if (!method.IsStatic)
            {
                if (singletonInstance)
                {
                    if (!_instances.TryGetValue(fullClassName, out instance))
                    {
                        instance = Activator.CreateInstance(type);
                        _instances[fullClassName] = instance!;
                    }
                }
                else
                {
                    instance = Activator.CreateInstance(type);
                }
            }

            var resultObj = method.Invoke(instance, parameters);

            // 异步方法处理
            if (resultObj is Task task)
            {
                await task.ConfigureAwait(false);

                var taskType = task.GetType();
                if (taskType.IsGenericType) // Task<T>
                {
                    var resultProperty = taskType.GetProperty("Result");
                    return resultProperty!.GetValue(task);
                }
                return null; // Task 无返回值
            }

            return resultObj;
        }
    }

Program.cs:

cs 复制代码
    internal class Program
    {
        static async Task Main(string[] args)
        {
            var executor = new ScriptExecutor();
            executor.Compile(GenerateCode());

            // 调用各种方法示例
            Console.WriteLine(await executor.InvokeAsync("DynamicCodeGenerate.HelloWorld", "OutPut")); // Count: 1
            Console.WriteLine(await executor.InvokeAsync("DynamicCodeGenerate.HelloWorld", "Add", new object[] { 5, 7 })); // 12
            Console.WriteLine(await executor.InvokeAsync("DynamicCodeGenerate.HelloWorld", "StaticMethod")); // 我是静态方法
            Console.WriteLine(await executor.InvokeAsync("DynamicCodeGenerate.HelloWorld", "AsyncMethod", new object[] { "Jack" })); // Hello, Jack
            Console.WriteLine(await executor.InvokeAsync("DynamicCodeGenerate.HelloWorld", "StaticAsyncAdd", new object[] { 10, 20 })); // 30

            // 多次调用实例方法,保持私有字段状态
            Console.WriteLine(await executor.InvokeAsync("DynamicCodeGenerate.HelloWorld", "OutPut")); // Count: 2
        }

        static string GenerateCode()
        {
            string code = @"
            using System;
            using System.Threading.Tasks;
            
            namespace DynamicCodeGenerate
            {
                public class HelloWorld
                {
                    private int _counter = 0;
            
                    public string OutPut()
                    {
                        _counter++;
                        return $""Count: {_counter}"";
                    }
            
                    public int Add(int a, int b)
                    {
                        return a + b;
                    }
            
                    public static string StaticMethod()
                    {
                        return ""我是静态方法"";
                    }
            
                    public async Task<string> AsyncMethod(string name)
                    {
                        await Task.Delay(50);
                        return $""Hello, {name}"";
                    }
            
                    public static async Task<int> StaticAsyncAdd(int a, int b)
                    {
                        await Task.Delay(50);
                        return a + b;
                    }
                }
            }";
            return code;
        }
    }
相关推荐
geovindu4 小时前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式
软件黑马王子7 小时前
20.Resources资源加载:基本实现
开发语言·前端框架·c#
张人玉8 小时前
基于 C# / .NET 8 + Windows Forms + SQLite 的桌面银行业务演示系统——华夏示范银行管理系统
windows·sqlite·c#·.net
1314lay_10078 小时前
C#调用Sql Server存储过程,并且使用传入表结构
数据库·经验分享·笔记·sqlserver·c#
慧都小妮子9 小时前
.NET 报表控件选型对比:Stimulsoft 与 FastReport 怎么选
c#·.net·报表开发·数据可视化·报表控件·报表引擎选型
1314lay_100711 小时前
C#调用Sql Server存储过程,有返回值的
数据库·sqlserver·c#
我是唐青枫11 小时前
C#.NET PInvoke 深入解析:封送、内存布局与原生互操作实战
开发语言·c#·.net
Am-Chestnuts11 小时前
AI 生成的数学公式,怎么用 DS随心转 导出成 Word 里能改的公式
人工智能·c#·word
geovindu11 小时前
CSharp: Strategy Pattern
开发语言·后端·c#·.net·.netcore·策略模式·行为模式
古少侠12 小时前
AI 内容粘到 Word 就乱?用 DS随心转 把复制的内容直接变成排版好的一段
人工智能·c#·word