环境: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;
}
}
