C# CSharpScript 的原理与应用

CSharpScript 是 Roslyn 编译器提供的脚本 API,允许在运行时动态执行 C# 代码。以下是其核心原理和典型应用场景:

一、核心原理

1. 架构基础

  • 基于 Roslyn 编译器即服务(Compiler as a Service)

  • 将代码文本实时编译为内存中的程序集

  • 通过反射或轻量级代码生成执行

2. 执行流程

源代码 → 语法分析 → 绑定 → 编译 → 内存程序集 → 执行

3. 关键技术

  • 增量编译:重复执行时复用部分编译结果

  • 脚本会话:通过 ScriptState 保持执行上下文

  • 对象传递:通过 globals 参数实现宿主与脚本的数据交换

二、基础应用

1. 基本执行

var result = await CSharpScript.EvaluateAsync("1 + 2");
Console.WriteLine(result); // 输出 3

2. 带上下文执行

public class Globals { public int X = 1, Y = 2; }
var globals = new Globals();

var result = await CSharpScript.EvaluateAsync<int>("X + Y", 
    globals: globals);

3. 多步执行

var state = await CSharpScript.RunAsync("int x = 1;");
state = await state.ContinueWithAsync("int y = 2;");
state = await state.ContinueWithAsync("x + y");
Console.WriteLine(state.ReturnValue); // 输出 3

三、高级特性

1. 自定义引用和导入

var options = ScriptOptions.Default
    .WithReferences(typeof(DateTime).Assembly)
    .WithImports("System.Math");

var result = await CSharpScript.EvaluateAsync<double>("Sqrt(4)", options);

2. 异常处理

try
{
    await CSharpScript.EvaluateAsync("throw new Exception(\"Test\");");
}
catch (CompilationErrorException e)
{
    Console.WriteLine(string.Join("\n", e.Diagnostics));
}

3. 性能优化

// 预编译脚本
var script = CSharpScript.Create<int>("X * Y", globalsType: typeof(Globals));
var compiled = script.Compile();

// 重复执行时使用编译结果
for (int i = 0; i < 100; i++)
{
    var result = await script.RunAsync(new Globals { X = i, Y = i });
}

四、典型应用场景

1. 动态规则引擎

var rule = "input.Age > 18 && input.Score > 60";
var result = await CSharpScript.EvaluateAsync<bool>(rule, 
    globals: new { input = new { Age = 20, Score = 70 } });

2. 公式计算器

var formula = "Math.Sin(x) + Math.Cos(y)";
var options = ScriptOptions.Default.WithImports("System.Math");
var calculate = ScriptOptions.Default
    .WithReferences(typeof(Math).Assembly);

var result = await CSharpScript.EvaluateAsync<double>(
    formula, globals: new { x = 1.0, y = 2.0 }, options);

3. 插件系统

string pluginCode = """
    using PluginBase;
    public class MyPlugin : IPlugin {
        public string Execute() => "Hello from plugin";
    }
    return new MyPlugin();
    """;

var options = ScriptOptions.Default
    .WithReferences(typeof(IPlugin).Assembly)
    .WithImports("PluginBase");

var plugin = await CSharpScript.EvaluateAsync<IPlugin>(pluginCode, options);
Console.WriteLine(plugin.Execute());

五、性能注意事项

  1. 编译开销:首次执行较慢(约100-500ms)

  2. 内存占用:每个脚本会生成内存程序集

  3. 最佳实践

    • 预编译高频使用的脚本

    • 复用 ScriptOptions 实例

    • 避免在循环中动态编译

六、安全限制

  1. 代码访问控制

var options = ScriptOptions.Default
    .WithReferences(/* 白名单程序集 */);
  1. 沙箱方案

AppDomain sandbox = AppDomain.CreateDomain("Sandbox");
try {
    // 在沙箱中执行脚本
}
finally {
    AppDomain.Unload(sandbox);
}

七、Helloworld

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

public class TestController
{
    public List<string> Output { get; set; } = new List<string>(); // 改为可读写
}

class Program
{
    static async Task<int> Main(string[] args)
    {
        var controller = new TestController();
        var remainingCodeTxt = """
            Output.Add("Hello,world!");
            return Output; 
            """;

        try
        {
            // 执行脚本
            var result = await CSharpScript.EvaluateAsync<List<string>>(
                remainingCodeTxt,
                globals: controller,
                options: ScriptOptions.Default
                    .WithImports("System.Collections.Generic")
                    .WithReferences(typeof(List<string>).Assembly));

            Console.WriteLine("Controller.Output: " + string.Join(", ", controller.Output));
            Console.WriteLine("脚本返回值: " + string.Join(", ", result));

            return 0;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return 1;
        }
    }
}

CSharpScript 为 C# 提供了强大的运行时代码执行能力,适用于需要动态性的场景,但需注意性能和安全性问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值